文章來源:http://www.dalbll.com/group/topic/asp.net/6333面試
在ASP.NET Core裏,咱們可使用構造函數注入很方便地對Controller,ViewComponent等部件作依賴注入。可是如何給過濾器ActionFilterAttribute也用上構造函數注入呢?緩存
問題
個人博客系統裏有個用來刪除訂閱文件緩存的ActionFilter,想要在發生異常的時候記錄日誌。個人博客用的日誌組件是NLog,所以不使用依賴注入的話,就直接使用LogManager.GetCurrentClassLogger()得到一個Logger的實例。整個過濾器的代碼以下:asp.net
1 public class DeleteSubscriptionCache : ActionFilterAttribute 2 { 3 private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); 4 5 public override void OnActionExecuted(ActionExecutedContext context) 6 { 7 base.OnActionExecuted(context); 8 DeleteSubscriptionFiles(); 9 } 10 11 private void DeleteSubscriptionFiles() 12 { 13 try 14 { 15 // ... 16 } 17 catch (Exception e) 18 { 19 Logger.Error(e, "Error Delete Subscription Files"); 20 } 21 } 22 }
而後在Action上去使用,和經典的ASP.NET MVC同樣ide
1 [Authorize] 2 [HttpPost, ValidateAntiForgeryToken, DeleteSubscriptionCache] 3 [Route("manage/edit")] 4 public IActionResult Edit(PostEditModel model)
這固然能夠沒有問題的運行,但寫代碼最重要的就是逼格,這個代碼耦合了NLog,而個人博客系統裏其餘地方早就在用ASP.NET Core的ILogger接口了。若是哪天日誌組件再也不用NLog了,那麼這個地方的代碼就得改,而使用ILogger接口的代碼就不須要動。雖然這種狀況是絕對不會發生的,可是寫代碼必定要有追求,儘量過分設計,才能不被人鄙視,而後才能面試造航母,工做擰螺絲。所以我決定把日誌組件用依賴注入的方式安排一下。函數
改造過濾器
方法和在Controller中使用依賴注入徹底同樣,咱們使用構造函數注入ILogger<DeleteSubscriptionCache>類型。因而代碼變成了這樣:spa
1 public class DeleteSubscriptionCache : ActionFilterAttribute 2 { 3 protected readonly ILogger<DeleteSubscriptionCache> Logger; 4 5 public DeleteSubscriptionCache(ILogger<DeleteSubscriptionCache> logger) 6 { 7 Logger = logger; 8 } 9 10 public override void OnActionExecuted(ActionExecutedContext context) 11 { 12 base.OnActionExecuted(context); 13 DeleteSubscriptionFiles(); 14 } 15 16 private void DeleteSubscriptionFiles() 17 { 18 try 19 { 20 // ... 21 } 22 catch (Exception e) 23 { 24 Logger.LogError(e, "Error Delete Subscription Files"); 25 } 26 } 27 }
可是問題來了,這樣的話咱們是無法在Action上無腦使用了,由於構造函數要求傳參。若是要本身new一個的話,裝逼就失敗了。咱們來看看正確的解決方法~.net
ServiceFilter
其實ASP.NET Core裏,咱們可使用ServiceFilter來完成這個需求。它也是一種Attribute,能夠做用在Action上。位於Microsoft.AspNetCore.Mvc.Core程序集裏,定義以下:設計
1 // A filter that finds another filter in an System.IServiceProvider. 2 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] 3 public class ServiceFilterAttribute : Attribute, IFilterFactory, IFilterMetadata, IOrderedFilter 4 { 5 public ServiceFilterAttribute(Type type); 6 public int Order { get; set; } 7 public Type ServiceType { get; } 8 public bool IsReusable { get; set; } 9 public IFilterMetadata CreateInstance(IServiceProvider serviceProvider); 10 }
ServiceFilter容許咱們解析一個已經添加到IoC容器裏的服務,所以咱們須要把DeleteSubscriptionCache註冊一下:
services.AddScoped<DeleteSubscriptionCache>();
而後就能直接使用了:日誌
1 [Authorize] 2 [HttpPost, ValidateAntiForgeryToken] 3 [ServiceFilter(typeof(DeleteSubscriptionCache))] 4 [Route("manage/edit")] 5 public IActionResult Edit(PostEditModel model)
運行時發現ILogger已經能被實例化了,完美!code