BFr*_*ree 77 c# asp.net-mvc dependency-injection actionfilterattribute asp.net-mvc-3
这是设置.假设我有一些需要服务实例的动作过滤器:
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
public void DoSomething(){}
}
Run Code Online (Sandbox Code Playgroud)
然后我有一个需要该服务实例的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);
}
}
Run Code Online (Sandbox Code Playgroud)
在MVC 1/2中,将依赖关系注入动作过滤器是一个痛苦的屁股.最常见的方法是使用自定义操作调用因为在这里可以看到: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);
}
}
Run Code Online (Sandbox Code Playgroud)
这里我们使用构造函数注入和重载构造函数来使用容器并注入服务.我确实同意将容器与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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在我知道一些纯粹主义者可能会嘲笑这一点,但严重的是,这将是什么缺点?它仍然是可测试的,因为你可以使用在测试时采用IMyService并以这种方式注入模拟服务的构造函数.由于您使用的是DependencyResolver,因此您没有被限制在DI容器的任何实现中,因此这种方法有任何缺点吗?
顺便提一下,这是使用新的IFilterProvider接口在MVC3中执行此操作的另一个好方法:http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in -Asp净MVC -3-
Mar*_*ann 91
是的,有一些缺点,因为IDependencyResolver本身存在很多问题,对于那些你可以添加使用Singleton Service Locator以及Bastard Injection的问题.
更好的选择是将过滤器实现为普通类,您可以在其中注入您想要的任何服务:
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
}
}
Run Code Online (Sandbox Code Playgroud)
请注意过滤器如何检查filterContext以确定是否应该应用该行为.
这意味着您仍然可以使用属性来控制是否应该应用过滤器:
public class MyActionFilterAttribute : Attribute { }
Run Code Online (Sandbox Code Playgroud)
但是,现在该属性完全是惰性的.
过滤器可以使用所需的依赖项组成,并添加到global.asax中的全局过滤器中:
GlobalFilters.Filters.Add(new MyActionFilter(new MyService()));
Run Code Online (Sandbox Code Playgroud)
有关此技术的更详细示例,尽管应用于ASP.NET Web API而不是MVC,请参阅此文章:http://blog.ploeh.dk/2014/06/13/passive-attributes
Str*_*ior 30
我不是肯定的,但我相信你可以使用一个空的构造函数(对于属性部分),然后有一个实际注入值的构造函数(对于过滤器部分).*
编辑:稍微阅读后,似乎可接受的方法是通过属性注入:
public class MyActionFilter : ActionFilterAttribute
{
[Injected]
public IMyService MyService {get;set;}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MyService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
Run Code Online (Sandbox Code Playgroud)
关于不使用服务定位器问题的原因:它主要只是降低了依赖注入的灵活性.例如,如果您正在注入日志记录服务,并且您希望自动为日志记录服务提供正在注入的类的名称,该怎么办?如果你使用构造函数注入,那将是很好的.如果您使用的是依赖性解析器/服务定位器,那么您将失去运气.
由于这被接受作为答案,我想记录下来说我更喜欢Mark Seeman的方法,因为它将Action Filter责任与Attribute分开.此外,Ninject的MVC3扩展还有一些非常强大的方法可以通过绑定配置动作过滤器.有关更多详细信息,请参阅以下参考
正如@usr在下面的注释中指出的那样,ActionFilterAttribute
s在加载类时被实例化,并且它们持续应用程序的整个生命周期.如果IMyService
接口不应该是Singleton,那么它最终成为一个Captive Dependency.如果它的实现不是线程安全的,那么你可能会遇到很多痛苦.
每当你的生命周期短于你班级的预期寿命时,最好注入一个工厂来按需生成依赖,而不是直接注入.
Mark Seemann建议的解决方案似乎很优雅.然而,对于一个简单的问 通过实现AuthorizeAttribute来使用框架感觉更自然.
我的解决方案是创建一个AuthorizeAttribute,其中包含一个静态委托工厂,用于在global.asax中注册的服务.它适用于任何DI容器,感觉稍微好于服务定位器.
在global.asax中:
MyAuthorizeAttribute.AuthorizeServiceFactory = () => Container.Resolve<IAuthorizeService>();
Run Code Online (Sandbox Code Playgroud)
我的自定义属性类:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public static Func<IAuthorizeService> AuthorizeServiceFactory { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return AuthorizeServiceFactory().AuthorizeCore(httpContext);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
37159 次 |
最近记录: |