Asp.net core自定義依賴注入容器,替換自帶容器
依賴注入
在asp.net core程式中,眾所周知,依賴注入基本上貫穿了整個項目,以通用的結構來講解,控制器層(Controller層)依賴業務層(Service層),業務層依賴於倉儲層(Repository層),而其他層級中也或多或少的使用了依賴注入,在這裡不過多的對於依賴注入概念上不進行講解,如果有不了解的同學,可以在微軟官網或者在搜索引擎搜索依賴注入相關概念,本文主要講解如何在asp.net core中實現自己的依賴注入容器,並且希望更多的同學能夠去閱讀源碼碼,因為源碼中暴露的一些抽象類或者介面向開發者提供了方便開發者自定義或者拓展的口子。好了,不多啰嗦,我們開始。
First IServiceProviderFactory介面
用過Autofac的同學都知道在asp.net core3版本之後,Autofac的使用方式稍微發生了一些變化,首先需要在Program.cs文件中需要使用Host.UseServiceProviderFactory方法,那實際上這個方法就是讓開發者能夠去實現自定義的依賴注入容器的一個拓展方法,我們可以查看一下這個方法的定義,注意看有個重點的介面就是IServiceProviderFactory介面,這個介面實際上是指定服務提供者的一個抽象工廠泛型介面,這是實現自定義IOC中最重要的介面之一,也是最開始的一步,實際上,實現自定義依賴注入容器,只需要實現兩個介面就可以實現自定義容器,可以看到這個方法有兩種參數機制一種是直接傳入對應的 實現類,另一種是使用委託的方式去創建對象,並且傳入了一個HostBuilderContext的對象,我們會使用這種方式去實現。
Second IServiceProvider介面
我們可以看到這個IServiceProviderFactory介面有兩個實現方法,一個是CreateBuilder方法,裡面傳入IServiceCollection變數,另一個方法是CreateServiceProvider方法,傳入我們這個介面指定的容器類的對象,其中IocContainer類不依賴於任何一個抽象,第一個方法的作用就是去構造這個容器的對象,需要返回我們指定的類型的對象,即是這個類是代表著容器,存放服務的,第二個方法是將上面構造的容器對象傳入進來,並且返回我們指定的服務提供者,那概念很清晰了,第一個IServiceProviderFactory介面是用來指定我們的容器是哪一個類是我們的容器,以及哪一個是我們的服務提供者,那實際上的IServiceProvider就是第二個重要的一個介面了,這個介面是只有一個方法,GetService方法,參數是一個Type,代表著我們是要去獲取哪一個類型的參數,返回值是Object,返回下層依賴者所需要的具體的一個對象。
Three 遵循規則實現自定義容器
那實際上自帶的依賴注入容器也是遵循這種規則去實現的,它提供了一個自帶的一個ServiceProvider的類去創建對象,那大家都知道啟動一個Core的一個程式,自帶的一些依賴對象都有一百多個,那大家可能會覺得,讓自己去寫這種一百多個對象的創建,並且類別還是core的開發者所沒有暴露出的類型,創建起來會很麻煩,並且還存在各種依賴,讓大家覺得可能自定義依賴注入容器可能很難,實際上,剛開始的時候我也是這麼想的,表達式樹在我去年十二月份的時候就開始寫程式碼了,只是今年才上傳到部落格,那實際上,自定義容器我也是去年開始研究的,剛開始也是寫了很多判斷因為它內部啟動的時候大的依賴了兩個東西一個是配置的IConfiguration,還有一個就是一個Host的一個類,下面又依賴了很多很多的類,感覺創建起來很麻煩,後來在想到了反射是可以獲取程式運行時的元數據並且去構造某個類型,那實際上,我們是可以用反射去實現一種投機取巧的方式去實現自定義依賴注入容器,那就是將啟動所依賴服務由自帶的ServiceProvider去進行提供和創建,一些項目開發中使用的服務由我們去進行管理,那說到這,已經有很多同學知道了怎麼去進行了,我們看程式碼。
上圖中,我們可以看到Provider類是實現了IServiceProvider的介面,並且實現了GetService的方法,可以看到,我使用的方式是去用反射去獲取自帶的ServiceProvider的構造函數,然後創建這個對象,並且在GetService方法中,首先去判斷能否從自帶的Provider去獲取和創建對象,如果獲取不到,那說明是我們項目中所需要的類型,從而使用我們自定義的容器去進行獲取對象,默認的獲取不到是因為我們在創建ServiceProvider對象的時候傳入了IServiceCollection的對象,這裡所包含的就是啟動Core程式所需要的依賴的集合,這樣我們就可以保證,程式啟動的時候是可以正常啟動的,然後在運行中,請求中所需要的服務類型是由我們自己去創建對象的,所以這樣就實現了簡單的IOC依賴注入容器,並且替換掉自帶的容器。
Four 控制器層的屬性注入以及拓展容器實現屬性注入和一介面多實現
按照我文章剛開始的時候所說,微軟給我們暴露了很多供我們自定義的介面和 抽象類,那如果需要在控制器層實現屬性注入那怎麼辦呢?那實際上還有一個介面,用來讓我們去創建控制器,那就是IControllerFactory介面,這個介面有兩個方法,一個是CreateController方法和ReleaseController方法,顧名思義就是一個是創建控制器,一個是銷毀控制器,那我們可以在第一個方法去實現控制器層的屬性注入以及一個介面多實現該怎麼去獲取的思路,如果是屬性注入,我們是需要去創建一個特性用來標記這個屬性是用來從容器中獲取對象的,我們可以在控制器層或者其他類中使用類似的方法去操作屬性注入賦值,如果是一個介面多實現呢,也是需要去定義兩個特性,一個特性標記在實現類上面,並且構造函數中有一個string類型的參數,用來標記是在容器中 注入的時候使用某個名稱用來標識這個類型,其次在我們進行獲取這個類型的時候需要在參數或者屬性用我們定義的第二個特性標記這個參數或者屬性是從容器中獲取的是哪一個名稱哪一個類型的對象,這樣就可以實現一個屬性注入和一個介面多實現的一個操作。
總結
以上是我個人實現自定義IOC的一個解決思路,並且在net core5以及net core6中實現,且5到6實現了無縫升級,沒有任何錯誤,希望能夠對各位讀者有所幫助。還是希望眾多道友能夠多解讀源碼,去查看core框架開發者提供給我們暴露給我們的自定義拓展的一些介面和抽象類。後面我依舊會持續更新core自定義相關的東西,會包括配置還有日誌等其他方面的東西,多執行緒方面的程式碼已經寫完,可以在QQ群6406277群文件中進行查找,也可以查看哪個net群有叫四川觀察的,那個就是我。IL後面我也寫了很多東西,後續也會一一奉上。在此,謝謝各位看官瀏覽。