11

У меня есть простой вопрос.контейнер IoC, проверьте наличие ошибок во время компиляции

Предположим, у меня есть .Net-решение с различными проектами, такими как библиотеки классов (bll, dal и т. Д.) И основной проект, который может быть веб-приложением или приложением wpf, это не имеет значения.

Теперь предположим, что я хочу использовать контейнер IoC (например, Windsor, Ninject, Unity и т. Д.) Для решения таких вещей, как валидаторы, репозитории, общие реализации интерфейса и т. Д.

Я собрал все это вместе. Компилирует и работает отлично. Затем, когда-нибудь, я добавляю новую услугу, и в своем коде я просто пытаюсь разрешить ее через контейнер IoC. Дело в том, что я забыл зарегистрировать его в конфигурации IoC.

Все компилируется, и приложение развертывается и запускается. Все работает нормально, за исключением случаев, когда код страницы запрашивает эту новую службу в контейнере, и контейнер отвечает «эй, я ничего не знаю об этой услуге».

Вы регистрируете свою ошибку и удобную страницу с ошибкой. Вы пойдете проверить ошибку, увидеть проблему и исправить ее. Довольно стандартный.

Теперь предположим, что мы хотим улучшить процесс и каким-то образом знать во время компиляции, если каждая служба, которую мы ожидаем, обрабатывает контейнер IoC, правильно зарегистрирована в коде.

Как это можно достичь? Одна вещь, Единичные тесты исключены из возможных ответов, я ищу другой способ, если он существует.

Мысли?

EDIT - После некоторых ответов и комментариев кажется, что Unit Tests - это единственный способ достичь этой функции.

Что я хотел бы знать, если бы Unit Tests были - по какой-либо причине - невозможны, и, таким образом, IoC не может быть протестирован в компилируемое время, это помешает вам использовать контейнер IoC и выбрать прямой экземпляр по всему вашему коду? Я имею в виду, считаете ли вы слишком опасным и рискованным использовать IoC и позднюю привязку, и увидите, что его преимущества превзойдены этим «недостатком»?

+2

Как отметил в своем ответе @ Chriseyre2000, Unit Testing - очень эффективный способ проверки конфигурации IoC, поэтому вы можете переоценить решение о том, что у вас нет тестов. Альтернативой этому является проверка вручную, что каждая страница на сайте загружается. – GolfWolf

+0

Это не решение, я хочу знать, есть ли другой последовательный и надежный способ достичь этого, кроме модульных тестов, который на данный момент является единственным способом, о котором я знаю. –

+0

Почему вы не сможете использовать единичный тест одного метода? – Chriseyre2000

ответ

8

Невозможно, чтобы компилятор подтвердил работу всей вашей программы. Тот факт, что ваша программа компилируется, не означает, что она работает правильно (даже без использования IoC). Для этого вам потребуются как автоматические тесты, так и ручное тестирование. Это не означает, что вы не должны пытаться позволить компилятору делать как можно больше, но оставаться в стороне от IoC по этой причине плохо, так как IoC предназначен для того, чтобы ваше приложение было гибким, проверяемым и поддерживаемым. Без IoC вы не сможете проверить свой код должным образом, и без каких-либо автоматических тестов практически невозможно написать любое поддерживаемое программное обеспечение разумного размера.

Имея гибкость, поскольку IoC обеспечивает, однако, означает, что зависимости, определенные некоторыми частями кода, больше не могут быть проверены компилятором. Поэтому вам нужно сделать это по-другому.

Некоторые каркасы DI позволяют проверить правильность контейнера. Simple Injector, например, содержит метод Verify(), который будет просто перебирать все регистрации и разрешать экземпляр для каждой регистрации. Вызывая этот метод (или используя аналогичный подход) во время запуска приложения, вы обнаружите во время тестирования (разработчика), если что-то не так с конфигурацией DI, и это предотвратит запуск приложения. Вы даже можете сделать это в модульном тесте.

Важно, однако, что тестирование конфигурации DI не требует значительного обслуживания. Если вы должны добавить единичный тест для каждого типа, который вы регистрируете для проверки контейнера, вы потерпите неудачу, просто потому, что недостающая регистрация (и, следовательно, пропущенный тест модуля) будет причиной отказа в первую очередь.

Это дает вам почти «поддержку времени компиляции». Однако вам нужно быть в курсе о дизайне вашего приложения и о том, как вы соединяете вещи. Вот несколько советов:

  1. Держитесь подальше от неявной инъекции свойств, где контейнеру разрешено пропускать инъекцию свойства, если он не может найти зарегистрированную зависимость. Это приведет к отказу вашего приложения от сбоя и приведет к появлению NullReferenceException s позже. Явная инъекция свойств, когда вы вынуждаете контейнер вводить свойство, в порядке, однако, по возможности используйте инъекцию конструктора.
  2. Зарегистрируйте все корневые объекты, если это возможно, явным образом. Например, зарегистрируйте все экземпляры ASP.NET MVC Controller явно в контейнере. Таким образом, контейнер может проверять полный граф зависимостей, начиная с корневых объектов. Вы должны зарегистрировать все корневые объекты автоматическим способом, например, используя отражение, чтобы найти все корневые типы. Например, MVC3 Integration NuGet Package простого инжектора содержит метод расширения RegisterMvcControllers, который сделает это за вас. Интеграционные пакеты других контейнеров содержат аналогичные функции.
  3. Если регистрация корневых объектов невозможна или возможна, проверьте создание каждого корневого объекта вручную во время запуска. Например, с классами ASP.NET Web Form Page вы, вероятно, вызовите контейнер из своего конструктора (поскольку классы Page, к сожалению, должны иметь конструктор по умолчанию). Ключ здесь снова находит их всех в использовании рефлексии. Найдя все классы страниц, используя отражение и создавая их, вы узнаете (во время запуска приложения или времени тестирования), есть ли проблема с вашей конфигурацией DI или нет.
  4. Пусть все сервисы, которыми управляет ваш контейнер IoC, у вас есть единый публичный конструктор. Несколько конструкторов приводят к двусмысленности и могут прервать ваше приложение непредсказуемыми способами. Имея multiple constructors is an anti-pattern.
  5. Существуют сценарии, в которых некоторые приложения не могут быть созданы во время запуска приложения. Чтобы гарантировать, что приложение может быть запущено в обычном режиме, а оставшаяся конфигурация DI еще может быть проверена, отбросьте эти зависимости за прокси-сервером или абстрактной фабрикой.
+0

Ваш ответ действительно оценен, но я думаю, что вы ошибаетесь, что я пытаюсь понять. Мне не нужно проверять, правильно ли разрешены мои зарегистрированные службы. Что нужно проверить, так это то, что каждая служба, запрашиваемая кодом, имеет регистрацию в конфигурации. И я хочу понять, может ли IoC считаться плохой или опасной практикой, эта вещь не может быть выполнена. –

+1

Вы не должны запрашивать свои услуги из своего кода. Вместо этого, инжектируйте все службы через инсталляцию конструктора и разрешите только корневые объекты. Таким образом, полный граф объектов строится за один раз, и таким образом вы знаете, что все службы зарегистрированы правильно, когда ваши корневые объекты разрешены правильно. – Steven

+0

Да, я имел в виду корневые объекты. –

2

Вы не можете протестировать весь свой код с помощью компилятора во время сборки. Это звучит абсурдно. В любом случае, вы должны написать разные тесты. Конфигурация контейнера является хорошим кандидатом для тестирования интеграции. Вы можете настроить тесты для автоматического запуска в качестве события после сборки, но это может увеличить время сборки. В любом случае это плохая причина для снижения использования контейнера IoC.

+0

Я с тобой полностью согласен. Это то, что я обычно делаю в любом случае, я просто хотел знать, была ли возможная альтернатива. –

+0

«Вы не можете протестировать весь свой код с помощью компилятора во время сборки. Это звучит абсурдно». Почему? Компилятор - это программа, отвечающая инструкциям. Не существует причин, по которым язык не может предоставить, не может поддерживать метапрограмму компиляции для тестирования. –

+1

Компилятор содержит только статический анализ кода. Но не запускает его. Регистрация и устранение компонентов происходит во время выполнения. –

3

Что я хотел бы знать, если бы Unit Tests были - по какой-либо причине - невозможны, и, следовательно, IoC не может быть протестирован в компилированное время, это помешает вам использовать контейнер IoC и выбрать прямая инстанция по всему вашему коду?

Вы представляете здесь false choice: либо используйте контейнер, либо «прямое инстанцирование по всему вашему коду». Но вы действительно можете практиковать инъекцию зависимостей без какого-либо контейнера.Вместо того, чтобы сделать это:

public void Main(string[] args) 
{ 
    var container = new Container(); 
    // ... register various types here... 

    // only Resolve call in entire application 
    var program = container.Resolve<Program>(); 

    program.Run(); 
} 

Вы просто сделать это:

public void Main(string[] args) 
{ 
    var c = new C(); 
    var b = new B(c); 
    var a = new A(b); 
    var program = new Program(a); 
    program.Run(); 
} 

Вы все еще можете получить все преимущества внедрения зависимостей таким образом: компоненты не создают друг друга и могут оставаться весьма развязаны. Конструкция компонентов остается ответственностью приложения composition root, хотя там не используется контейнер. Вы также получаете проверку времени компиляции, которую ищете. больше

Два замечания:

  1. вы помечены свой вопрос dependency-injection, поэтому я предполагаю, что вы действительно используете инъекции зависимостей, в отличие от шаблона Service Locator. Другими словами, я предполагаю, что вы не подвергаете и не вызываете контейнер по всему вашему коду, что необязательно, и not recommended. Мой ответ больше не работает, если вы используете Service Locator. Пожалуйста, считайте это забастовкой против Service Locator, а не против моего ответа. Constructor injection - лучший выбор.

  2. программисты обычно предпочитают использовать контейнер вместо этого ручного подхода, потому что в большом приложении он может получить нетривиальность, чтобы сохранить порядок экземпляров правильно - потребуется много переупорядочения просто потому, что вы вводите единственная новая зависимость где-то. Контейнеры также предлагают дополнительные функции, которые облегчают жизнь.

+0

Вы правы, я не занимаюсь сервисом Locator, но DI, поскольку он более масштабируемый и удобный. Я считаю, что Service Locator - действительно антипаттерн.Я имею тенденцию иметь экземпляр контейнера singleton с общим методом Resolve , который будет использовать мой контейнер IoC по своему усмотрению. Я прошу его разрешить объект T, и он будет его создавать, разрешая свои зависимости через DI. Мой вопрос заключался в понимании того, что утверждение «IoC плохое, потому что ваш код компилируется, но может быть сломан» содержит любую воду, и если была альтернатива Unit Test (потому что тесты считаются сложностью). –

+0

@MatteoMosca: я не ответил на этот вопрос здесь? Я продемонстрировал, что вы можете иметь IoC и время компиляции без проведения единичных тестов. –

+0

Да и нет. Я не хочу отказываться от контейнера или рамки IoC в пользу этого. Я хотел знать, может ли моя «потребность» быть удовлетворена в указанных условиях, и после всех ответов и комментариев довольно ясно, что ответ отрицательный, что я и думал с самого начала. Спасибо за помощь. –

Смежные вопросы