这是设置。假设我有一些需要服务实例的操作过滤器:
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
public void DoSomething(){}
}
然后我有一个需要该服务实例的 ActionFilter:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService; // <--- How do we get this injected
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
在 MVC 1/2 中,将依赖项注入(inject) Action 过滤器有点麻烦。最常见的方法是使用自定义操作调用程序,如下所示:http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/这种解决方法背后的主要动机是因为以下方法被认为是草率的并且与容器紧密耦合:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService;
public MyActionFilter()
:this(MyStaticKernel.Get<IMyService>()) //using Ninject, but would apply to any container
{
}
public MyActionFilter(IMyService myService)
{
_myService = myService;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
这里我们使用构造函数注入(inject)并重载构造函数以使用容器并注入(inject)服务。我同意容器与 ActionFilter 紧密耦合。
但我的问题是:现在在 ASP.NET MVC 3 中,我们对正在使用的容器进行了抽象(通过 DependencyResolver),是否仍然需要所有这些环路?请允许我演示:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService;
public MyActionFilter()
:this(DependencyResolver.Current.GetService(typeof(IMyService)) as IMyService)
{
}
public MyActionFilter(IMyService myService)
{
_myService = myService;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
现在我知道一些纯粹主义者可能会对此嗤之以鼻,但说真的,这有什么缺点呢?它仍然是可测试的,因为您可以使用在测试时采用 IMyService 的构造函数并以这种方式注入(inject)模拟服务。由于您使用的是 DependencyResolver,因此您不会受限于任何 DI 容器的实现,那么这种方法有什么缺点吗?
顺便说一句,这是在 MVC3 中使用新的 IFilterProvider 接口(interface)执行此操作的另一种好方法:http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3
最佳答案
是的,有缺点,因为有lots of issues with IDependencyResolver本身,对于那些你可以添加使用 Singleton服务定位器,以及 Bastard Injection .
更好的选择是将过滤器实现为一个普通类,您可以在其中注入(inject)任何您想要的服务:
public class MyActionFilter : IActionFilter
{
private readonly IMyService myService;
public MyActionFilter(IMyService myService)
{
this.myService = myService;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if(this.ApplyBehavior(filterContext))
this.myService.DoSomething();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
if(this.ApplyBehavior(filterContext))
this.myService.DoSomething();
}
private bool ApplyBehavior(ActionExecutingContext filterContext)
{
// Look for a marker attribute in the filterContext or use some other rule
// to determine whether or not to apply the behavior.
}
private bool ApplyBehavior(ActionExecutedContext filterContext)
{
// Same as above
}
}
请注意过滤器如何检查 filterContext 以确定是否应应用该行为。
这意味着您仍然可以使用属性来控制是否应用过滤器:
public class MyActionFilterAttribute : Attribute { }
但是,现在该属性是完全惰性的。
过滤器可以由所需的依赖组成,并添加到 global.asax 中的全局过滤器:
GlobalFilters.Filters.Add(new MyActionFilter(new MyService()));
有关此技术的更详细示例,尽管适用于 ASP.NET Web API 而不是 MVC,请参阅本文:http://blog.ploeh.dk/2014/06/13/passive-attributes
关于c# - 将依赖项注入(inject) ASP.NET MVC 3 操作过滤器。这种方法有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7192543/