我的目标:在DI提供的情况下注入IFilterProvider,但默认情况下回退到全局FilterProviders.Providers.GetFilters()方法。
Internet上有很多资源(包括Microsoft的“官方”资源)展示了如何将IFilterProvider接口注入到类中。但是,它们都使用服务定位器反模式(根据Mark Seeman)来执行。这是我找到的列表:
http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvcdependencyinjection_topic4.aspx
http://merbla.blogspot.com/2011/02/ifilterprovider-using-autofac-for-mvc-3.html
http://thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3
IFilterProvider and separation of concerns
那么这些方法有什么问题呢?他们都将DI容器注入到目标类中,而不是相反。如果您在类中看到诸如container.Resolve()之类的内容,那么您就不会对DI容器产生对象生存期的控制权,这实际上就是控制权的反转。
此外,尝试创建回退到全局FilterProviders.Providers实例的问题在于,尽管它与IFilterProvider接口具有相同的签名,但实际上并未实现此接口。那么,如何将全局静态成员作为逻辑默认值注入并仍然允许使用DI覆盖它呢?
最佳答案
创建一个基于IFilterProvider的包装器类,该包装器类返回全局FilterProviders.Providers.GetFilters()结果,如下所示:
public class FilterProvider
: IFilterProvider
{
#region IFilterProvider Members
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor);
}
#endregion
}
然后,在需要依赖项的类中调整构造函数。
public class MyBusinessClass
: IMyBusinessClass
{
public MyBusinessClass(
IFilterProvider filterProvider
)
{
if (filterProvider == null)
throw new ArgumentNullException("filterProvider");
this.filterProvider = filterProvider;
}
protected readonly IFilterProvider filterProvider;
public IEnumerable<AuthorizeAttribute> GetAuthorizeAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = filterProvider.GetFilters(controllerContext, actionDescriptor);
return filters
.Where(f => typeof(AuthorizeAttribute).IsAssignableFrom(f.Instance.GetType()))
.Select(f => f.Instance as AuthorizeAttribute);
}
}
接下来,将您的DI容器配置为使用FilterProvider具体类型作为默认类型。
container.Configure(x => x
.For<IFilterProvider>()
.Use<FilterProvider>()
);
如果您需要覆盖默认值并提供自己的IFilterProvider,现在只需要基于IFilterProvider创建一个新的具体类型并交换在DI容器中注册的内容即可。
//container.Configure(x => x
// .For<IFilterProvider>()
// .Use<FilterProvider>()
//);
container.Configure(x => x
.For<IFilterProvider>()
.Use<NewFilterProvider>()
);
最重要的是,此模式中的任何地方都没有服务定位器。
关于c# - 没有服务定位器的IFilterProvider注入(inject),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15048030/