获取Simple Injector的容器实例

Cha*_*ani 16 c# asp.net-mvc dependency-injection simple-injector

我正在使用Simple Injector和ASP.NET MVC项目.我添加了SimpleInjector.Integration.Web.Mvcnuget包.这会SimpleInjectorInitializerApp_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));
}
Run Code Online (Sandbox Code Playgroud)

这样可以正确配置MVC控制器的DI.

我的问题是,如果我想在任何controller\class中获取容器的实例来手动解决某些依赖,我该怎么办呢.

我之前曾在AutoFac上工作,它有一个依赖接口IComponentContext,可以注入任何需要手动执行任何分辨率的类.

更新:

这是一个场景.我的控制器使用的服务初始化取决于在控制器方法中传递的输入参数,因此在构造期间不能实例化依赖性.

我知道这对于DI来说有点反模式,但是在很少的地方需要它,因此注入DI容器是下一个最好的事情.简单的注入器样本应该使用静态变量来共享我想要避免的容器,并且通过SimpleInjectorInitializer工作方式也是不可能的.

Ste*_*ven 31

除了作为应用程序启动路径一部分的任何代码之外,任何代码都不应直接依赖于容器(或容器抽象,容器外观等).这种模式被称为服务定位马克·西曼有一个很好的解释为什么这是一个坏主意.

因此组件(例如控制器)不应该直接依赖于容器,因为这会隐藏使用的依赖关系并使类更难以测试.此外,您的代码开始依赖于外部框架(使其更难更改)或依赖于它不需要了解的抽象.

我的控制器使用的服务初始化取决于在控制器方法中传递的输入参数,因此在构造期间无法实例化依赖项

这个问题有一个普遍的模式:抽象的工厂设计模式.工厂模式允许您延迟类型的创建,并允许您传递额外的运行时参数以构造特定类型.执行此操作时,您的控制器不必依赖于Container,它可以防止您必须在单元测试中传入构造的容器(DI框架通常不应在单元测试项目中使用).

但请注意,在创建过程中让组件需要运行时数据是一种代码味道.防止那样做.

您可能认为通过这样做我们只是将问题转移到工厂实现.虽然我们正在将对容器的依赖性转移到工厂实现中,但实际上我们解决了这个问题,因为工厂实现将是应用程序的组合根的一部分,它允许应用程序代码本身无视任何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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在组合根(启动路径)中,我们定义工厂实现及其注册:

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>();
}
Run Code Online (Sandbox Code Playgroud)

在创建时,Container寄存器本身(使用调用RegisterSingle<Container>(this)),因此您始终可以将容器注入任何组件.这类似于IComponentContext在使用Autofac时注入.但同样适用于Autofac,Simple Injector和任何其他容器:您不希望将容器注入位于组合根之外的组件(并且几乎没有理由).