2016-10-01 93 views
2

我對使用IOC容器還有點新意,而且我正在努力一點。我正在使用ASP.NET MVC 5.2與Ninject.MVC3。我有一個異常過濾器,基本上丟給日誌服務:如何在ASP.NET MVC的RegisterGlobalFilters方法中執行依賴注入

public class ExceptionLoggerFilter : IExceptionFilter 
{ 
    private readonly ILogService _logService; 

    public ExceptionLoggerFilter(ILogService logService) { 
     _logService = logService; 
    } 

    public void OnException(ExceptionContext filterContext) { 
     _logService.LogError(filterContext.Exception); 
    } 
} 

我想使用依賴注入來獲取日誌服務的實例。然而,RegisterGlobalFilters方法是靜態的:

public class FilterConfig 
{ 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     filters.Add(new ExceptionLoggerFilter(???)); 
    } 
} 

我可以用一個靜態構造函數的一個FilterConfig,但這只是不覺得自己是一個很好的解決方案。

我嘗試使用性能噴射在ExceptionLoggerFilter類,但它只是沒有工作(LogService總是空):

[Inject] 
public ILogService LogService { get; set; } 

我已經在使用工廠模式讀了,但事實並非如此似乎在這裏幫助,因爲我仍然需要將Factory類注入到FilterConfig類的靜態構造函數中。我可能是錯的,但是這也不是正確的解決方案。

var logService = (ILogService)DependencyResolver.Current.GetService(typeof(ILogService)); 
filters.Add(new ExceptionLoggerFilter(logService)); 

這工作,但它是一個很好的解決方案:

現在,我使用的是剛剛放棄的我,一個實例方法?有沒有更好的辦法?我對依賴注入和IOC容器的想法都搞砸了嗎?

回答

0

您應該先註冊ExceptionLoggerFilter類型爲IExceptionFilter
然後添加以下內容:

public class FilterConfig 
{ 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     var logger = DependencyResolver.Current.GetService<IExceptionFilter>(); 
     filters.Add(logger); 
    } 
} 
+2

這樣做通常是一個壞主意,除非您確定知道'IExceptionFilter'註冊(及其所有依賴項)ar因爲MVC全局過濾器是全局存儲的,這意味着它們變成了單例。 – Steven

3

MVC全局過濾器只是一個實例的列表。這意味着這種過濾器的任何依賴關係也會變成單例。這意味着您應該非常小心使用這種方法,因爲在您的應用程序中意外導致Captive Dependency真的很容易。圈養依賴往往難以跟蹤,往往只能在測試或生產中彈出。

相反,您應該創建一個代理類,該代理類可以在使用過濾器時將其委託回您的容器/內核,因此它可以當場解析實際的過濾器。這可以防止圈養相關性。

這樣的代理可以看看如下:

public class NinjectExceptionFilterProxy<TExceptionFilter> : IExceptionFilter 
    where TExceptionFilter : IExceptionFilter 
{ 
    private readonly Kernel _kernel; 

    public ExceptionLoggerFilter(Kernel kernel) { 
     _kernel = kernel; 
    } 

    public void OnException(ExceptionContext filterContext) { 
     var filter = _kernel.Get<TExceptionFilter>(); 
     filter.OnException(filterContext); 
    } 
} 

可以按如下方式註冊代理服務器的過濾器:

public static void RegisterGlobalFilters(GlobalFilterCollection filters, IKernel kernel) 
{ 
    filters.Add(new NinjectExceptionFilterProxy<ExceptionLoggerFilter>(kernel)); 
} 
0

你可以做到這一點通過構建自定義過濾器供應商,讓您攔截創建過濾器的過程,在這個階段你可以注入你的依賴關係。

下面是一個團結的樣本,你可以很容易地將其轉換爲NInject

的過濾器供應商創建一個類

public class UnityActionFilterProvider : FilterAttributeFilterProvider 
{ 
    private readonly IUnityContainer container; 

    public UnityActionFilterProvider(IUnityContainer container) 
    { 
     this.container = container; 
    } 


    public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     var filters = base.GetFilters(controllerContext, actionDescriptor); 

     foreach (var filter in filters) 
     { 
      container.BuildUp(filter.Instance.GetType(), filter.Instance); 
     } 

     return filters; 
    } 
} 

接下來,在FilterConfig.cs,更新RegisterGlobalFilters使用新的過濾器提供者

public class FilterConfig 
    { 
     public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
     { 
      var oldProvider = FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider); 
      FilterProviders.Providers.Remove(oldProvider); 

      var container = UnityConfig.GetConfiguredContainer(); 
      var provider = new UnityActionFilterProvider(container); 
      FilterProviders.Providers.Add(provider); 

      filters.Add(new HandleErrorAttribute()); 
     } 
    }