我有一个全局过滤器属性,需要访问按HTTP请求注册的项目:
// other ContainerBuilder stuff
builder.RegisterType<HttpDependency>().As<IHttpDependency>().InstancePerHttpRequest();
其他地方:
internal sealed class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// EVIL YUCKY SERVICE LOCATOR!
var resolved = AutofacDependencyResolver.Current.RequestLifetimeScope.Resolve<IHttpDependency>();
if (resolved.NeedsRedirect)
{
// does a redirect
}
base.OnActionExecuting(filterContext);
}
}
将其注册为全局过滤器:
// in FilterConfig.cs
filters.Add(new MyActionFilter());
由于这是一个全局过滤器,我不能使用构造函数注入,即应用程序启动时的HTTP上下文不应该被重用于每个请求。如何在不通过服务定位器伸出并抓住它的情况下正确连接它?
答案 0 :(得分:1)
一种方法是从Attribute中删除逻辑,并在实现IActionFilter的类中实现它。然后在容器中注册该类,以便依赖注入将正常工作。 Orchard CMS使用这种方法。
public class MyCustomActionFilterAttribute : Attribute
{
}
public class MyCustomActionFilter : FilterProvider, IActionFilter
{
protected MyService Service { get; private set; }
// MyService can be injected by the container...
public MyCustomActionFilter(MyService service)
{
this.Service = service;
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// Check to see if the action has a matching attribute
var attributes = filterContext.ActionDescriptor.GetCustomAttributes(typeof(MyCustomActionFilterAttribute), true);
// Perform some logic here....
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
可以创建一个将过滤器应用于动作的IActionInvoker,这个类使用DependencyResolver自动实例化我的MVC。
public class FilterResolvingActionInvoker : ControllerActionInvoker
{
protected IEnumerable<IFilterProvider> Providers { get; private set; }
// Filters registered with the container are injected by the container
public FilterResolvingActionInvoker(IEnumerable<IFilterProvider> providers)
{
this.Providers = providers;
}
// Add the filter to the current FilterInfo
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
foreach (var provider in this.Providers)
{
provider.AddFilters(filters);
}
return filters;
}
}
定义一个允许我们注册过滤器的通用接口。
public interface IFilterProvider
{
void AddFilters(FilterInfo filterInfo);
}
public abstract class FilterProvider : IFilterProvider
{
public void AddFilters(FilterInfo filterInfo)
{
if (this is IActionFilter)
{
filterInfo.ActionFilters.Add(this as IActionFilter);
}
}
}
并将其注册到容器构建器。也可以为Autofac创建一个扩展方法,以自动注册程序集中的所有IFilterProviders。
builder.RegisterType<FilterResolvingActionInvoker>().As<IActionInvoker>().InstancePerDependency();
builder.RegisterType<MyCustomActionFilter>().As<IFilterProvider>().InstancePerDependency();
答案 1 :(得分:-1)
与往常一样,避免定位器的一个选项是拥有一个在Compositon Root中设置的本地工厂。工厂设置为使用您的ioc容器。
http://netpl.blogspot.com/2012/12/di-factories-and-composition-root.html
虽然您可以说“技术上”这个“看起来像”定位器(您创建一个工厂实例并要求它提供服务),但它不会向任何其他基础结构引入任何依赖关系,包括您最终使用的实际IoC容器实现工厂 - 实际工厂的实现是Composition Root的一部分(在全局应用程序类附近)。
这种方法导致许多孤立的小工厂负责部分基础架构,但每个工厂仍然有一个可插拔的提供程序,您可以在Composition Root附近实现,从而避免任何外部依赖。