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>() 
); 

最重要的是,在這種模式的任何地方都沒有服務定位器。