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

Получить экземпляр контейнера для простого инжектора

Я использую Simple Injector с проектом ASP.NET MVC. Я добавил пакет SimpleInjector.Integration.Web.Mvc nuget. Это добавляет класс SimpleInjectorInitializer в папку App_Start и инициализирует DI. Код выглядит примерно так:

public static void Initialize()
{
    // Did you know the container can diagnose your configuration? 
    // Go to: https://simpleinjector.org/diagnostics
    var container = new Container();

    //Container configuration code
    DependencyResolver.SetResolver(
        new SimpleInjectorDependencyResolver(container));
}

Это правильно конфигурирует DI для контроллера MVC.

Мой вопрос: если я хочу получить экземпляр контейнера в любом из классов controller\class, чтобы разрешить некоторую зависимость вручную, как я могу это сделать.

Ранее я работал над AutoFac и имел интерфейс зависимостей IComponentContext, который можно вводить в любой класс, который должен выполнять любое разрешение вручную.

Обновление

Вот сценарий. Мой контроллер использует службу, инициализация которой зависит от входного параметра, переданного в методе контроллера, и, следовательно, зависимость не может быть создана во время построения.

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

4b9b3361

Ответ 1

За исключением любого кода, который является частью пути запуска приложения, код не должен зависеть непосредственно от контейнера (или абстракции контейнера, фасада контейнера и т.д.). Этот шаблон называется "Локатор сервисов" и Mark Seemann имеет хорошее объяснение, почему это плохая идея.

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

Мой контроллер использует службу, инициализация которой зависит от ввода параметр, переданный в методе контроллера, и, следовательно, зависимость не может быть создан во время строительства

Существует общий шаблон для этой проблемы: абстрактный factory шаблон дизайна. Шаблон factory позволяет задерживать создание типов и позволяет передавать дополнительные параметры времени выполнения для построения определенного типа. Когда вы это делаете, ваш контроллер не должен зависеть от контейнера, и это мешает вам проходить в сконфигурированном контейнере в ваших модульных тестах (рамки DI обычно не должны использоваться в ваших проектах unit test).

Обратите внимание, что для того, чтобы ваши компоненты требовали данные времени выполнения во время создания, это запах кода. Предотвратите это.

Вы можете подумать, что, делая это, мы просто переместим проблему в реализацию factory. Хотя мы перемещаем зависимость от контейнера в реализацию factory, мы фактически решаем проблему, потому что реализация factory будет частью приложения Composition Root, который позволяет самому программному коду не обращать внимания на любую инфраструктуру DI.

Так вот как я советую вам структурировать ваш код:

// Definition of the factory in the UI or BL layer
public interface ISomeServiceFactory
{
    ISomeService Create(int inputParameter);
}

// Controller depending on that factory:
public class MyController : Controller
{
    private readonly ISomeServiceFactory factory;

    public MyController(ISomeServiceFactory factory)
    {
        this.factory = factory;
    }

    public ActionResult Index(int value)
    {
        // here we use that factory
        var service = this.factory.Create(value);
    }
}

В вашем корневом каталоге (путь запуска) мы определяем реализацию factory и регистрацию для него:

private class SomeServiceFactory : ISomeServiceFactory
{
    private readonly Container container;

    // Here we depend on Container, which is fine, since
    // we're inside the composition root. The rest of the
    // application knows nothing about a DI framework.
    public SomeServiceFactory(Container container)
    {
        this.container = container;
    }

    public ISomeService Create(int inputParameter)
    {
        // Do what ever we need to do here. For instance:
        if (inputParameter == 0)
            return this.container.GetInstance<Service1>();
        else
            return this.container.GetInstance<Service2>();
    }
}

public static void Initialize()
{
    var container = new Container();

    container.RegisterSingle<ISomeServiceFactory, SomeServiceFactory>();
}

После создания Container регистрируется (используя вызов RegisterSingle<Container>(this)), чтобы вы всегда могли вставлять контейнер в любой компонент. Это похоже на инъекцию IComponentContext при работе с Autofac. Но то же самое относится к Autofac, Simple Injector и к любому другому контейнеру: вы не хотите вводить свой контейнер в компоненты, расположенные вне корня композиции (и вряд ли когда-либо для этого причина).