Контейнер IoC, проверка ошибок во время компиляции

У меня простой вопрос.

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

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

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

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

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

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

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

мысли?

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

Что я хотел бы знать, если модульные тесты были - по какой - либо причине-невозможны, и, следовательно, IoC не может быть протестирован во время компиляции, это помешает вам использовать контейнер IoC и выбрать прямой экземпляр по всему вашему коду? Я имею в виду, считаете ли вы слишком небезопасным и рискованным использовать МОК и позднюю привязку, и видите, что его преимущества перекрываются этим "недостатком"?

3 ответов


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

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

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

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

это дает вам "почти" времени компиляции поддержки. Тем не менее, вы должны быть осведомлены о дизайне вашего приложения и о том, как вы проводите вещи вместе. Вот несколько советов:

  1. держитесь подальше от неявной инъекции свойств, где контейнер разрешен пропустить инъекцию свойства, если оно не может найти зарегистрированную зависимость. Это позволит запретить вашему приложению быстро отказать и приведет к NullReferenceExceptions позже. Явная инъекция свойств, где вы заставляете контейнер вводить свойство, прекрасна, однако используйте инъекцию конструктора, когда это возможно.
  2. зарегистрируйте все корневые объекты явно, если это возможно. Например, зарегистрировать все ASP.NET MVC Controller экземпляры явно в контейнере. Таким образом, контейнер может проверить полный график зависимостей, начиная с корневых объектов. Необходимо автоматически зарегистрировать все корневые объекты, например с помощью отражения, чтобы найти все корневые типы. The MVC3 интеграция NuGet пакет простого инжектора, например, содержит RegisterMvcControllers метод расширения, который сделает это за вас. Пакеты интеграции других контейнеров содержат аналогичные функции.
  3. если регистрация корневых объектов невозможна или невозможна, проверьте создание каждого корневой объект вручную во время запуска. С ASP.NET веб-форма Page классы, например, вы, вероятно, вызовете контейнер из своего конструктора (так как Page классов, к сожалению, должны иметь конструктор по умолчанию). Ключ здесь снова найти их все в один раз, используя отражение. Найдя все классы страниц с помощью отражения и создания их экземпляров, вы узнаете (во время запуска приложения или тестирования), есть ли проблема с конфигурацией DI или нет.
  4. давайте все службы, которыми управляет контейнер IoC, имеют один открытый конструктор. Несколько конструкторов приводят к неоднозначности и могут нарушить ваше приложение непредсказуемыми способами. Имея несколько конструкторов является анти-шаблон.
  5. существуют сценарии, в которых некоторые зависимости еще не могут быть созданы во время запуска приложения. Чтобы гарантировать, что приложение может быть запущено нормально, а остальная часть конфигурации DI все еще может быть проверена, абстрактные те зависимости за прокси-сервером или абстрактной фабрикой.

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

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

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();
}

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

больше двух Примечания:

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

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


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