2

我的目标:在DI提供一个IFilterProvider的情况下注入IFilterProvider,但默认情况下回退到全局FilterProviders.Providers.GetFilters()方法。IFilterProvider注入没有服务定位器

互联网上有很多资源(包括一个“官方”微软版本),演示了如何将IFilterProvider接口注入到类中。但是,他们都使用服务定位器反模式(每个Mark Seeman)来执行此操作。下面是我发现的人的名单:

  1. http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvcdependencyinjection_topic4.aspx
  2. http://merbla.blogspot.com/2011/02/ifilterprovider-using-autofac-for-mvc-3.html
  3. http://thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3
  4. IFilterProvider and separation of concerns

那么,有什么不对这些方法呢?他们都将DI容器注入目标类而不是其他方式。如果你在你的类中看到类似container.Resolve()的东西,那么你不会控制对象到DI容器的生命周期,这正是控制反转的真正目的。

此外,尝试创建回退到全局FilterProviders.Providers实例的问题是,虽然它具有与IFilterProvider接口相同的签名,但它实际上并未实现此接口。那么如何将全局静态成员注入为逻辑默认值并仍允许使用DI来覆盖它?

+0

只要您将该类放入组合根,就可以在类中引用您的容器。 – 2013-11-15 18:03:18

+0

同意。但那只会是配置代码,而不是应用程序代码。就我而言,IFitlerProvider是开源DLL的API的一部分,它无法控制组合根。 – NightOwl888 2013-11-15 20:21:41

回答

2

创建基于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>() 
); 

最重要的是,在这种模式的任何地方都没有服务定位器。