C# 获取Simple Injector的容器实例
我在ASP.NETMVC项目中使用SimpleInjector。我添加了C# 获取Simple Injector的容器实例,c#,asp.net-mvc,dependency-injection,simple-injector,C#,Asp.net Mvc,Dependency Injection,Simple Injector,我在ASP.NETMVC项目中使用SimpleInjector。我添加了SimpleInjector.Integration.Web.Mvcnuget包。这将在App\u Start文件夹中添加SimpleInjectorInitializer类,并初始化DI。代码看起来像 publicstaticvoidinitialize() { //您知道容器可以诊断您的配置吗? //转到:https://simpleinjector.org/diagnostics var container=新容器()
SimpleInjector.Integration.Web.Mvc
nuget包。这将在App\u Start
文件夹中添加SimpleInjectorInitializer
类,并初始化DI。代码看起来像
publicstaticvoidinitialize()
{
//您知道容器可以诊断您的配置吗?
//转到:https://simpleinjector.org/diagnostics
var container=新容器();
//容器配置代码
DependencyResolver.SetResolver(
新的SimpleInjectorDependencyResolver(容器));
}
这将正确配置MVC控制器的DI
我的问题是,如果我想在任何controller\class中获取容器的实例以手动解决某些依赖关系,我如何才能做到这一点
我之前在AutoFac上工作过,它有一个依赖接口IComponentContext
,可以将它注入到需要手动执行任何解析的任何类中
更新:
下面是一个场景。我的控制器使用一个服务,该服务的初始化依赖于控制器方法中传递的输入参数,因此在构造期间无法实例化依赖关系
我知道这在某种程度上是DI的一种反模式,但在少数地方它是必需的,因此注入DI容器是次好的事情。简单注入器示例应该使用静态变量来共享我想要避免的容器,而且通过
SimpleInjectorInitializer
的工作方式也是不可能的。除了作为应用程序启动路径一部分的任何代码外,任何代码都不应该直接依赖于容器(或容器抽象、容器外观等)此模式被调用,并有一个“为什么这是一个坏主意”的解释
因此,组件(如控制器)不应直接依赖于容器,因为这会隐藏所使用的依赖项并使类更难测试。此外,您的代码开始依赖于外部框架(使其更难更改)或依赖于它不需要知道的抽象
我的控制器使用一个服务,该服务的初始化取决于输入
在控制器方法中传递的参数以及依赖项
无法在构造期间实例化
这个问题有一个通用模式:工厂模式允许您延迟类型的创建,并允许您为特定类型的构造传入额外的运行时参数。当您这样做时,您的控制器不必依赖于容器,它可以防止您必须传入已构造的容器r单元测试(DI框架通常不应在单元测试项目中使用)
但是请注意,让您的组件需要。防止这样做
您可能会认为,通过这样做,我们只是将问题转移到工厂实现中。虽然我们将容器的依赖性转移到工厂实现中,但实际上我们正在解决问题,因为工厂实现将是应用程序的一部分,这允许应用程序代码本身不受ny 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);
}
}
在合成根目录(启动路径)中,我们定义工厂实现和注册:
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>();
}
私有类SomeServiceFactory:ISomeServiceFactory
{
专用只读容器;
//这里我们依赖于容器,这很好,因为
//我们在构图根中,其余部分
//应用程序对DI框架一无所知。
公共服务工厂(容器)
{
this.container=容器;
}
公共服务创建(int inputParameter)
{
//做我们在这里需要做的事情。例如:
如果(inputParameter==0)
返回此.container.GetInstance();
其他的
返回此.container.GetInstance();
}
}
公共静态void Initialize()
{
var container=新容器();
container.RegisterSingle();
}
创建时,
容器
会注册自身(使用调用RegisterSingle(this)
)因此,您始终可以将容器注入任何组件。这类似于在使用Autofac时注入IComponentContext
。但Autofac、Simple Injector和任何其他容器也是如此:您不希望将容器注入位于合成根目录之外的组件(几乎没有理由这样做).您可以注入容器,但不应该。在这种情况下,您滥用容器作为一个。为什么您需要在该控制器中使用容器?添加了一个需要支持的场景Hanks Steve,是的,我理解这一部分,并应用了与您在此处解释的相同的模式。但由于工厂本身需要容器,我使用AutoFacIComponentContext
。我不知道如何在SimpleInjector中执行同样的操作。除了Steven的出色回答之外,我们还可以轻松地使用泛型返回ISomeService
(而不是使用if/else硬编码)的具体实现,方法是使用以下命令:public T Create(),其中T:ISomeService{return(T)this.container.GetInstance(typeof(T));}
。然后使用:var service=this.factory.Create();
。您还需要使用以下命令更新ISomeServiceFactory
接口:T Create()其中T:ISomeService;
。我可以看出,理论上为您的代码正在创建的所有类型创建工厂接口是一个好主意。但实际上,可能有一些代码需要创建许多不同类型的对象。然后,您可以使用一个可以实际为您创建许多不同类型的接口。一些基本原理利斯