除了属于应用程序启动路径一部分的任何代码之外,任何代码都不应直接依赖于容器(或容器抽象、容器外观等)。这种模式称为服务定位器 http://martinfowler.com/articles/injection.html and 马克·西曼 http://blog.ploeh.dk/ has a 很好的解释 http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/为什么这是一个坏主意。
因此,组件(例如控制器)不应直接依赖于容器,因为这会隐藏所使用的依赖项并使类更难以测试。此外,您的代码开始依赖于外部框架(使其更难以更改)或依赖于它不需要了解的抽象。
我的控制器使用一个服务,该服务的初始化取决于输入
控制器方法中传递的参数以及依赖关系
无法在构造期间实例化
这个问题有一个通用模式:抽象工厂设计模式 https://en.wikipedia.org/wiki/Abstract_factory_pattern。工厂模式允许您延迟类型的创建,并允许您传入额外的运行时参数来构造特定类型。当您这样做时,您的控制器不必依赖于 Container,并且它可以防止您必须在单元测试中传递构造的容器(DI 框架通常不应在单元测试项目中使用)。
但请注意,让您的组件需要创建期间的运行时数据是一种代码味道 https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99。防止这样做。
您可能认为这样做只是将问题转移到工厂实现上。尽管我们将对容器的依赖转移到工厂实现中,但实际上我们正在解决问题,因为工厂实现将成为应用程序的一部分成分根 http://blog.ploeh.dk/2011/07/28/CompositionRoot/,它允许应用程序代码本身忽略任何 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>();
}
创建后,Container
注册自身(使用调用RegisterSingle<Container>(this)
)因此您始终可以将容器注入到任何组件中。这类似于注入IComponentContext
使用 Autofac 时。但这同样适用于 Autofac、Simple Injector 和任何其他容器:您不希望将容器注入到位于组合根之外的组件中(而且几乎没有理由这么做)。