1. 程式人生 > >ASP.NET Core預設容器實現Controller的屬性注入

ASP.NET Core預設容器實現Controller的屬性注入

- 僅針對Controller的屬性注入; - 使用預設容器,不依賴第三方庫; ## 故事背景   閒來無事給專案做優化,發現大多數Controller裡面都會用到Logger和AutoMapper,每個Controller都建構函式注入,感覺重複勞動太多了,ASP.NET Core預設容器也並不支援屬性注入。寫到基類裡面通過構造注入`ServiceProvider`進行獲取?這樣每次都要傳遞ServiceProvider,也並不方便。嗯。。。有沒有辦法使用預設容器實現Controller的屬性注入呢。本著有問題先搜一搜的態度,找到了一篇相關[文章](https://www.cnblogs.com/hohoa/p/11884719.html)。   文中使用替換`IControllerActivator`,並將Controller手動新增到DI容器(可以直接使用`AddControllersAsServices`拓展方法)的方式實現了屬性注入。也就是說必須使用從DI獲取Controller的方式,才能使用這種屬性注入的方式。不過之前看過一篇文章(找不到了。。。)裡面的回覆,大意為`ASP.NET Core預設不使用DI獲取Controller,是因為DI容器構建完成後就不能變更了,但是Controller是可能有動態載入的需求的`。嗯。。。本著最大相容性的考慮,看看有沒有`不干涉Controller建立方式`也能實現這個功能的辦法。 ## 想了個邏輯   首先確認目的:`在不改變ControllerActivator功能的前提下,為其生成的Controller設定屬性`。具體的邏輯就有點繞了:我們需要實現一個設定Controller屬性的`IControllerActivator`(一個靜態代理類),並替換掉DI容器中的`IControllerActivator`宣告,但仍然需要DI容器構建之前已宣告的`ControllerActivator`(因為我們不知道具體如何構建),並且在我們實現的`IControllerActivator`中訪問(作為被代理的物件)。 針對每個邏輯: - 實現一個設定Controller屬性的`IControllerActivator`靜態代理:這個很好寫; - 用靜態代理類替換掉DI容器中的`IControllerActivator`宣告:這個也很簡單; - DI容器保留之前已宣告的`ControllerActivator`:這個可以直接將`IControllerActivator`宣告中的實現型別作為服務新增到`ServiceCollection`中; - 在靜態代理類中訪問上一步新增到`ServiceCollection`的`IControllerActivator`具體實現:這個問題有點小麻煩。因為靜態代理類和原始的`IControllerActivator`實現類都需要使用DI容器構建,那麼靜態代理類只能通過建構函式注入`IControllerActivator`實現類,但是我們沒辦法在寫程式碼的時候就確定要注入的型別,也不能直接依賴`IControllerActivator`(自己依賴自己了)。想來想去。。。嗯。。。我們可以使用泛型實現代理類的邏輯,並在新增服務描述時動態生成一個泛型型別。 ## 寫程式碼 首先需要一個`ASP.NET Core`專案。 #### 關鍵程式碼 嗯。。屬性注入的關鍵點,只要有`ServiceProvider`,其它的就好說了。 - 那就先寫一個`IServiceProviderSetter`放在這裡 ```C# using System; public interface IServiceProviderSetter { void SetServiceProvider(IServiceProvider serviceProvider); } ``` - 實現泛型靜態代理 ```C# using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; public class ControllerActiva