我正在使用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));
}
这可以正确配置MVC控制器的DI。
我的问题是,如果我想在任何controller \ class中获取容器的实例来手动解决某些依赖关系,我该怎么办呢。
我之前曾在AutoFac上工作,并且它有一个依赖接口IComponentContext
,可以将其注入任何需要手动执行任何解析的类。
更新:
这是一个场景。我的控制器使用的服务初始化取决于在控制器方法中传递的输入参数,因此在构造期间不能实例化依赖性。
据我所知,这对于DI来说有些反模式,但是在很少的地方需要它,因此注入DI容器是最好的选择。简单的Injector样本应该使用静态变量来共享我想要避免的容器,而且SimpleInjectorInitializer
的工作方式也不可能。
答案 0 :(得分:31)
除了作为应用程序启动路径一部分的任何代码之外,任何代码都不应直接依赖于容器(或容器抽象,容器外观等)。这种模式称为Service Locator,Mark Seemann有good explanation为什么这是一个坏主意。
所以组件(例如控制器)不应该直接依赖于容器,因为这会隐藏使用的依赖项并使类更难以测试。此外,您的代码开始依赖于外部框架(使其更难更改)或依赖于它不需要了解的抽象。
我的控制器使用初始化取决于输入的服务 参数在控制器方法中传递,因此依赖性 在施工期间无法实例化
这个问题存在一般模式:abstract factory design pattern。工厂模式允许您延迟类型的创建,并允许您传递额外的运行时参数以构造特定类型。执行此操作时,您的控制器不必依赖于Container,它可以防止您必须在单元测试中传入构造的容器(DI框架通常不应在单元测试项目中使用)。
请注意,让您的组件需要runtime data during creation is a code smell。防止这样做。
您可能认为通过这样做我们只是将问题转移到工厂实施。虽然我们正在将容器的依赖性转移到工厂实现中,但实际上我们解决了这个问题,因为工厂实现将是应用程序Composition Root的一部分,它允许应用程序代码本身无视任何DI框架。 / p>
所以我建议您构建代码:
// 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>();
}
创建后,Container
会自行注册(使用调用RegisterSingle<Container>(this)
),因此您始终可以将容器注入任何组件。这类似于在使用Autofac时注入IComponentContext
。但同样适用于Autofac,Simple Injector和任何其他容器:您不希望将容器注入位于组合根之外的组件(并且几乎没有理由)。