我们有一个基于 ASP.NET MVC 4 的应用程序,该应用程序已有几年历史,我正在努力消除它的一些技术债务。我正在做的事情之一是引入依赖注入,以便我们可以更好地将业务逻辑与数据访问实现分开,并减少编写隔离单元测试的痛苦。我使用了简单注射器,但遇到了一些问题。
我已经按照简单注入器文档中的 MVC 集成指南 http://simpleinjector.readthedocs.org/en/latest/mvcintegration.html。它描述了这样的初始化过程:
- 创建一个容器
- 在容器中注册类型
- 验证容器(可选)
- 覆盖默认的依赖解析器
因此,这是迄今为止在应用程序中实现的方式。为了清楚起见,我删除了日志记录语句,并为上述步骤添加了标记注释:
// 1
var container = new Container();
var webRequestLifestyle = new WebRequestLifestyle();
// 2
container.Register<IOrganizationService>(
delegate
{
var proxy = new OrganizationServiceProxy(
organizationServiceManagement, clientCredentials);
proxy.EnableProxyTypes();
return proxy;
},
webRequestLifestyle);
container.RegisterSingle<ILoggerProvider>(LoggerProvider); // static field
container.Register<IExternalLinkRepository, ExternalLinkRepository>(webRequestLifestyle);
container.Register<IQueueRepository, QueueRepository>(webRequestLifestyle);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.RegisterMvcIntegratedFilterProvider();
// 3
container.Verify(VerificationOption.VerifyAndDiagnose);
// 4
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
当尝试实例化 MVC 控制器时,上述代码在步骤 3 中失败,抛出异常System.InvalidOperationException: An error occurred when trying to create a controller of type 'MyProject.MyNamespace.MyController'. Make sure that the controller has a parameterless public constructor.
这是有道理的,因为我的控制器是为构造函数注入设置的。例如:
public MyController(ILoggerProvider loggerProvider)
{
Logger = loggerProvider.Get(GetType());
}
默认的 MVC 控制器激活器不知道如何处理这个问题。然而,我不明白的是为什么Container.Verify
Simple Injector 的方法完全命中了 MVC 的默认控制器激活器。容器不应该天生使用简单注入器的依赖解析来测试依赖图吗?查看异常调用堆栈,它起源于System.Web.Mvc.DefaultControllerFactory.DefaultControllerActivator.Create
,所以它在某些时候肯定超出了简单注入器的范围。
但是,当我交换步骤 3 和 4 的顺序时:
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
container.Verify(VerificationOption.VerifyAndDiagnose);
它成功验证了容器,并且依赖项注入在应用程序中也按预期工作。这似乎暂时解决了问题。尽管如此,我还是想知道:
- 为什么 Simple Injector 使用 MVC 的默认控制器激活器来测试依赖解析?这是预期/记录在任何地方吗?
- 首先设置自定义解析器然后验证是否有任何副作用?我这么问是因为这与文档中的指南相反。它似乎按预期工作,如果其中任何一个失败,应用程序无论如何都会崩溃,所以从应用程序的角度来看,这似乎并不重要。