如何使用 Simple Injector 装饰 ASP.NET MVC 控制器
How to decorate an ASP.NET MVC controller with Simple Injector
我想对我的 MVC 控制器应用一些横切关注点。目前,这是通过抽象基础 class 实现的,但是随着我们重构更多代码库以利用依赖注入,我想知道 Simple Injector 是否可以帮助我通过它装饰或拦截设施。
所以我尝试创建一个非常基本的装饰器:
public class ControllerDecorator : IController
{
private readonly IController _controller;
public ControllerDecorator(IController controller)
{
_controller = controller;
}
public void Execute(RequestContext requestContext)
{
// Do something of a cross-cutting nature here...
_controller.Execute(requestContext);
}
}
在我的作文根中:container.RegisterDecorator<IController, ControllerDecorator>()
但是,我的装饰器 Execute
方法中的代码似乎从未被调用过。是不是因为MVC框架直接解析了我的控制器classes而不是通过IController
?那样的话,我该怎么办?我在这里错过了什么?
在默认配置中,您不能将装饰器应用于控制器。这样做的原因是 MVC 的 DefaultControllerFactory
通过具体类型请求控制器。因为它请求具体类型,Simple Injector 无法应用装饰器;它必须假设调用者需要这个具体类型,因此必须 return 这个确切的类型(或子类型)。
要解决此问题,您必须将默认值 DefaultControllerFactory
替换为自定义值:
public class SimpleInjectorControllerFactory : DefaultControllerFactory {
public IDictionary<Type, InstanceProducer> Producers { get; set; }
protected override IController GetControllerInstance(RequestContext rc, Type type) {
return (IController)this.Producers[type].GetInstance();
}
}
接下来,在您的引导程序中,您必须将对 RegisterMvcControllers
的调用替换为以下内容:
var controllerTypes = SimpleInjectorMvcExtensions.GetControllerTypesToRegister(
container, Assembly.GetExecutingAssembly());
var controllerProducers = controllerTypes
.ToDictionary(type => type, type => CreateControllerProducer(container, type));
// Verify after creating the controller producers.
container.Verify();
ControllerBuilder.Current.SetControllerFactory(
new SimpleInjectorControllerFactory { Producers = controllerProducers });
CreateControllerProducer
方法如下所示:
private static InstanceProducer CreateControllerProducer(Container c, Type type) {
var producer = Lifestyle.Transient.CreateProducer(typeof(IController), type, c);
producer.Registration.SuppressDiagnosticWarning(
DiagnosticType.DisposableTransientComponent,
"MVC disposes the controller when the web request ends.");
return producer;
}
关键部分是 CreateProducer
的调用由 typeof(IController)
提供;这允许 Simple Injector 为 IController
.
应用装饰器
就是这样;现在您可以为 IController
.
注册装饰器了
不过有一个警告:Web API 和新的 ASP.NET 核心都无法将装饰器应用于控制器。两个框架都需要具体的类型;如果你包装真正的控制器,它们会坏掉。使用这些框架装饰控制器的首选方法是通过 OWIN 管道。所以这个答案只适用于 MVC 3、4 和 5。
我想对我的 MVC 控制器应用一些横切关注点。目前,这是通过抽象基础 class 实现的,但是随着我们重构更多代码库以利用依赖注入,我想知道 Simple Injector 是否可以帮助我通过它装饰或拦截设施。
所以我尝试创建一个非常基本的装饰器:
public class ControllerDecorator : IController
{
private readonly IController _controller;
public ControllerDecorator(IController controller)
{
_controller = controller;
}
public void Execute(RequestContext requestContext)
{
// Do something of a cross-cutting nature here...
_controller.Execute(requestContext);
}
}
在我的作文根中:container.RegisterDecorator<IController, ControllerDecorator>()
但是,我的装饰器 Execute
方法中的代码似乎从未被调用过。是不是因为MVC框架直接解析了我的控制器classes而不是通过IController
?那样的话,我该怎么办?我在这里错过了什么?
在默认配置中,您不能将装饰器应用于控制器。这样做的原因是 MVC 的 DefaultControllerFactory
通过具体类型请求控制器。因为它请求具体类型,Simple Injector 无法应用装饰器;它必须假设调用者需要这个具体类型,因此必须 return 这个确切的类型(或子类型)。
要解决此问题,您必须将默认值 DefaultControllerFactory
替换为自定义值:
public class SimpleInjectorControllerFactory : DefaultControllerFactory {
public IDictionary<Type, InstanceProducer> Producers { get; set; }
protected override IController GetControllerInstance(RequestContext rc, Type type) {
return (IController)this.Producers[type].GetInstance();
}
}
接下来,在您的引导程序中,您必须将对 RegisterMvcControllers
的调用替换为以下内容:
var controllerTypes = SimpleInjectorMvcExtensions.GetControllerTypesToRegister(
container, Assembly.GetExecutingAssembly());
var controllerProducers = controllerTypes
.ToDictionary(type => type, type => CreateControllerProducer(container, type));
// Verify after creating the controller producers.
container.Verify();
ControllerBuilder.Current.SetControllerFactory(
new SimpleInjectorControllerFactory { Producers = controllerProducers });
CreateControllerProducer
方法如下所示:
private static InstanceProducer CreateControllerProducer(Container c, Type type) {
var producer = Lifestyle.Transient.CreateProducer(typeof(IController), type, c);
producer.Registration.SuppressDiagnosticWarning(
DiagnosticType.DisposableTransientComponent,
"MVC disposes the controller when the web request ends.");
return producer;
}
关键部分是 CreateProducer
的调用由 typeof(IController)
提供;这允许 Simple Injector 为 IController
.
就是这样;现在您可以为 IController
.
不过有一个警告:Web API 和新的 ASP.NET 核心都无法将装饰器应用于控制器。两个框架都需要具体的类型;如果你包装真正的控制器,它们会坏掉。使用这些框架装饰控制器的首选方法是通过 OWIN 管道。所以这个答案只适用于 MVC 3、4 和 5。