0

Такой вопрос. Может кто нибудь объяснить как работает DI контейнер в .net? Допустим, я хочу реализовать ioc для nunit тестов: как должен выглядеть DI контейнер? И как правильно создавать экземпляры нужных мне классов, чтобы в их конструкторы автоматически передавались нужные сервисы? В .net core framework объявление экземпляров классов (контроллеров, например) скрыто где-то в дебрях промежуточного ПО, по этому я не совсем понимаю как все это работает. Как именно сервисы попадают в конструкторы? И как можно это повторить в другом проекте, чтобы можно было автоматизировать передачу сервисов в конструкторы, как в фреймворке? Буду благодарен за любую инфу.

  • 1
    Почитайте об инверсии зависимостей, а потом про DI. То о чем вы пишете - встроенный DI в .NET ASP, а не весь .net core framework. Есть сторонние DI например "Autofac" его можно юзать как в том же ASP, так и в других видах приложений. Про DI есть тут – Pavel Popov Dec 06 '21 at 19:58
  • 1
    реализовать ioc для nunit тестов - так не надо, юнит тесты не должны нуждаться в DI контейнере по идее – tym32167 Dec 06 '21 at 20:07
  • 1
  • Всем спасибо, разобрался. В основном, благодаря хорошей документации от autofac. – vitalik198 Dec 07 '21 at 11:09

1 Answers1

1

Если вопрос о принципе работы, то к примеру у вас есть 2 класса

public class Settings
{
    public string Text { get; set; }
}

public class Worker { private readonly Settings _settings;

public Worker(Settings settings)
{
    _settings = settings;
}

}

Если без контейнера, то запустить всю эту систему весьма просто

var settigns = new Settings();
var worker = new Worker(settings);

Но допустим у вас есть абстрактный контейнер, я возьму синтаксис только что придуманного мной. У него есть билдер ContainerBuilder с обобщенным методом Register, и есть он сам Container с обобщенным методом Resolve.

Тогда выше показанный код будет выглядеть так.

var builder = new ContainerBuilder();
builder.Register<Settings>();
builder.Register<Worker>();

var container = builder.Build(); var worker = container.Resolve<Worker>();

Здесь видно, что вся работа с контейнером разделяется на 2 этапа: регистрация типов и получение экземпляров.

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

Внутри это работает так: контейнер через рефлексию ищет доступный конструктор и смотрит типы его аргументов. Далее он смотрит у себя, есть ли среди зарегистрированных типов нужные? Если да, то он создает экземпляр нужного типа и далее просто вызывает конструктор используя только созданный экземпляр для передачи в аргумент. Это называется Dependency Injection (DI), а именно Constructor Injection. Если зарегистрированного типа не найдено, контейнер просто бросит исключение и ничего не создаст.

Конечно всё намного сложнее в популярных контейнерах устроено, но смысл инверсии управления тот же. Управление созданием экземпляров автоматизировано внутри контейнера, вы сами в коде ничего не создаете.

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

Когда-то я сам разбирался с этим вопросом здесь: IoC в MVVM для чайников

aepot
  • 49,560