Подтвердить что ты не робот

IoC, проверить ошибки во время компиляции

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

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

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

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

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

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

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

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

Мысли?

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

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

4b9b3361

Ответ 1

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

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

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

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

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

  • Держитесь подальше от неявной инъекции свойств, когда контейнеру разрешено пропустить инъекцию свойства, если он не может найти зарегистрированную зависимость. Это приведет к отказу вашего приложения от сбоя и приведет к NullReferenceException позже. Явная инъекция свойств, в которой вы вынуждаете контейнер вводить свойство, является прекрасной, однако, когда это возможно, используйте инъекцию конструктора.
  • Зарегистрируйте все корневые объекты, если это возможно, явным образом. Например, зарегистрируйте все экземпляры ASP.NET MVC Controller явно в контейнере. Таким образом, контейнер может проверять полный граф зависимостей, начиная с корневых объектов. Вы должны зарегистрировать все корневые объекты автоматическим способом, например, используя отражение, чтобы найти все корневые типы. Например, пакет интеграции NuGet MVC3 для простого инжектора содержит метод расширения RegisterMvcControllers, который сделает это за вас. Интеграционные пакеты других контейнеров содержат аналогичные функции.
  • Если регистрация корневых объектов невозможна или возможна, проверьте создание каждого корневого объекта вручную во время запуска. Например, с классами ASP.NET Web Form Page вы, вероятно, вызовите контейнер из своего конструктора (поскольку классы Page, к сожалению, должны иметь конструктор по умолчанию). Ключ здесь снова находит их всех в использовании рефлексии. Найдя все классы страницы, используя отражение и создавая их, вы узнаете (во время запуска или тестирования приложения), есть ли проблема с вашей конфигурацией DI или нет.
  • Пусть все сервисы, которыми управляет ваш контейнер IoC, имеют один открытый конструктор. Несколько конструкторов приводят к двусмысленности и могут прервать ваше приложение непредсказуемыми способами. несколько конструкторов - это анти-шаблон.
  • Существуют сценарии, в которых некоторые зависимости еще не могут быть созданы во время запуска приложения. Чтобы гарантировать, что приложение может быть запущено в обычном режиме, а остальная конфигурация DI еще может быть проверена, отбросьте те зависимости за прокси или абстрактными factory.

Ответ 2

Что я хотел бы знать, если бы Unit Tests были - по какой-либо причине - невозможны, и, следовательно, 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();
}

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

Еще два замечания:

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

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

Ответ 3

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