.NET Core中的IoC和DI

1 本期文章介紹
  • IoC和DI的簡單含義
  • IoC的理解與認知
  • DI的理解與認知
  • IoC與DI的關係
  • 使用IoC/DI的好處
  • 在.NET Core中使用IoC/DI
2 往期文章
3 IoC和DI的簡單含義 IoC,意爲控制反轉,英文(Inversion of Control),它不是一種技術,而是一種設計思想,一個重要的面向對象編程的法則。IoC意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。 DI,意爲依賴注入,英文(Dependency Injection)。組件之間的依賴關係由容器在運行期間決定,即由容器動態地將依賴項注入到組件當中,經過依賴注入機制,咱們有時候只須要簡單地配置,無需修改代碼便可完成自身邏輯,而沒必要去關心依賴的具體資源,姓甚名誰,由何處來,又去往何處。 4 IoC的理解與認知 咱們先來思考幾個問題,既然是叫IoC(控制反轉),那麼
  • 是誰控制誰?
  • 控制了什麼?
  • 爲何叫反轉?難道還有正轉?
咱們一個一個來進行分析。
  • 是誰控制誰?控制了什麼?傳統程序當中,咱們定義的一個A類,須要另外一個B類時,直接就在A類的內部經過new進行建立依賴的b對象了,是咱們的程序主動去建立依賴對象。而IoC它的核心思想就是有一個容器專門來建立這些依賴的對象,即由IoC容器來控制依賴的對象的建立。誰控制了誰?那固然是IoC容器控制了對象;控制了什麼?那就是控制了外部資源的獲取(不僅僅是依賴對象的建立。還有什麼文件資源啊等等的)
  • 爲何是反轉,而不是正傳?在傳統應用程序當中,是由咱們本身再對象內部去直接建立獲取依賴的對象,也就是正轉;而反轉則是由容器來幫忙去建立對象及注入依賴對象,對象只是被動的接收依賴對象,因此是反轉。哪些方面被反轉了?依賴對象的獲取方式被反轉了。之前是主動出擊,實例化依賴對象,如今是注入依賴對象,被動接受。
  • 其實IoC容器,它就至關於一個專門來建立對象的工廠,你要什麼對象,他就給你什麼對象。有了IoC容器,依賴關係就變了,原先的依賴關係就沒有了,他們都依賴於IoC容器了,經過IoC容器來創建他們之間的關係。下面經過幾個圖示例子來解析IoC思想。
  • 傳統程序設計當中,用戶類依賴於用戶信息類,都是主動去建立相關對象再組合起來,客戶端向服務器發送請求以後經歷了這三個過程:用戶類的建立、用戶信息類的建立,將用戶信息類主動注入到用戶類。
  • 而當有了IoC/DI容器後,客戶端獲取這些服務,再也不主動去索取了,再也不主動去建立這些對象了。IoC會作這些動做:建立用戶類,看用戶類是否有依賴對象,有的話,首先建立依賴對象,以後再將其注入到用戶類當中。由容器掌管這些對象的生命週期。
  • 咱們能夠再試想一下,在一個大型項目一個模塊當中,有這樣若干個類,就4個吧,object a,b,c,d,a依賴於b,b依賴於c,c依賴於d,甚至還可能交叉依賴,這時候咱們引入了第三方「IoC」,使得a,b,c,d這四個對象之間沒有了耦合關係,以下圖:齒輪之間的傳動所有依靠「第三方」。所有對象的控制權上繳給IoC,由IoC來分配對象(-。-)因此,IoC容器就成了整個系統的核心,它相似於粘合劑,把全部的對象都給粘合在了一塊兒,這樣子相輔相成,發揮粗最大的做用。若是沒有它,對象彼此之間就失去了聯繫。
5 DI的理解與認知 一樣的,在學習DI以前,咱們先來思考這樣幾個問題,既然是叫DI(依賴注入),那麼:
  • 是誰依賴於誰?
  • 爲何須要依賴?
  • 誰注入誰?
  • 注入了什麼?
下面咱們來一一分析
  • 誰依賴於誰:固然是應用程序依賴於IoC容器;
  • 爲何須要依賴:應用程序須要IoC容器來提供對象須要的外部資源
  • 誰注入誰:很明顯是IoC容器注入應用程序
  • 注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、數據等等)
依賴注入,一般有兩種方式:
  • 設置注入
  • 構造注入
一般來講,依賴注入是爲了控制反轉,就是說不須要咱們本身去new服務實例了。 6 IoC與DI的關係
  • 他們之間的關係實際上是同一個概念的不一樣角度描述,依賴注入是爲了實現控制反轉,「依賴注入」明確描述了「被注入對象依賴IoC容器,配置依賴對象」。
7 IoC/DI的好處
  • 沒有引入IOC以前,對象A依賴於對象B,那麼對象A在初始化或者運行到某一點的時候,A直接使用new關鍵字建立B的實例,程序高度耦合,效率低下,不管是建立仍是使用B對象,控制權都在本身手上。
  • 傳統的代碼,每一個對象負責管理與本身須要依賴的對象,致使若是須要切換依賴對象的實現類時,須要修改不少地方。同時,過分耦合也使得對象難以進行單元測試。
  • 依賴注入把對象的創造交給容器去管理,很好的解決了代碼緊耦合(tight couple)的問題,是一種讓代碼實現鬆耦合(loose couple)的機制。
  • 鬆耦合讓代碼更具靈活性,能更好地應對需求變更,以及方便單元測試
說了這麼多,咱們來實戰一下IoC/DI 8 在.NET Core中使用依賴注入
  • 新建Web Core API應用程序,刪除自帶的weather控制器和類,新建空api控制器Student,新建方法,返回學生的問候語,而且啓動監聽,dotnet watch run,以便調試:
  • 瀏覽器中輸入:localhost:5000/api/Student,測試以下圖:
  • 在控制器當中,咱們返回了一個學生的姓名。可是在實際開發當中,咱們的業務邏輯部分是和控制器分開的,因而,咱們新建一個倉儲類庫Repository,新建倉儲服務類StudentRepository,新增業務方法,返回學生問候語:
  • 如今問題來了,隨着實際業務開發,咱們的業務類當中的方法會愈來愈多,同名或者相似的方法數不勝數,咱們直接定位進去就是一大堆方法和方法體,因此咱們修改一下,根據面向接口編程原則,咱們將其抽離,新建倉儲接口類庫,IRepository,新建接口類,IStudentRepository,原先的方法繼承實現接口:
  • 更改控制器中的方法:
  • 測試以下:
  • 如今是很簡單的一個demo,在實際當中,咱們的業務層中依賴着衆多的對象,好比數據庫、日誌、等等相關的一些資源,下面咱們舉個例子,在業務類StudentRepository中依賴着一個Other類型的對象,用於獲取天氣。
  • 那這樣很明顯,咱們的控制器在new的時候,須要
 
  • 而在實際當中,咱們的業務類是依賴了衆多的對象,這只是一個控制器一個依賴對象,假如是不少個控制器,不少個依賴對象呢,這樣設計明顯不方便,並且不利於測試,接下來咱們使用依賴注入。
  • 如上圖,在Startup類的ConfigureServices方法當中注入。ConfigureServices這個東西是專門爲咱們的服務作準備工做的,怎麼說呢,你警察執法須要警棍吧;大媽掃地須要掃帚吧,ConfigureServices裏面其實就是爲咱們的服務注入所須要的工具,咱們對象此時並無說是被實例化,它只在你須要的時候,實例化,並注入到須要的對象當中去。
  • 舉個生活不恰當的例子哈,嘿嘿。之前咱們找男女友,都是直接上,談戀愛,後來人多了,出現了婚介服務(IoC)容器。這我的啊,如今不須要本身去找女友了,你想要什麼樣的女友?去婚介所,中介所,登記一下資料,給出要求,交錢;他就在給你搜羅,你須要女友,他主動給你介紹,注入到你的生活當中去。這裏中介在他們那登陸的形形色色的人的資料,就存放在那。當你須要,他就new一個出來,如今再也不是你主動出擊,而是被迫接收了,固然你也能夠拒絕哈哈,這就比如,程序,擬注入錯了對象,我並不須要這個對象,是那種!
  • 咱們使用services.AddScoped<接口類,實現類>()的方式注入,這個是net Core自帶的IoC容器三大生命週期注入方式之一,爲何說自帶的呢,由於後邊咱們還會用到第三方容器,另外兩個分別是Singleton單例模式注入、Transient瞬時模式注入;
  • Singleton 每次首次請求以後,實例化,以後全部的請求都講沿用這個服務對象。
  • Scoped 每次請求以後,都講實例化一個對象,生命週期貫穿整次請求,每次請求使用的服務對象不是同一個。
  • Transient 服務在每次請求時被建立,它最好被應用於輕量級無狀態服務(至於啥是輕量級無狀態服務,嘿嘿,我也不知道。。。~)
  • 下面更改控制器實例方式,改成構造函注入。
  • 運行一下,結果以下圖:
  • 能夠看到,它報了一個錯,不可以加載others這個服務,這是爲何呢;由於咱們的業務類那裏的構造函數注入了others這個對象,可是卻沒有在容器當中注入,接下來,咱們將others進行一下注入:
  • 注意,ConfigureService裏面的注入是不分前後順序的,由於你不管是先給大媽發掃帚仍是先給警察發警棍,這一切都是要準備就緒以後,整個應用才能夠提供服務,由於這是準備工做,全部的準備就緒以後,服務纔會開始運轉,因此準備工做的前後順序是不區分的。再次運行測試以下圖:
  • 這時服務已經成功運行,那麼如今還有一個問題,在實際當中,咱們的接口和實現等服務確定不止這一個,難道咱們要service.add這樣一直下去?不要擔憂,有咱們的第三方容器,autofac
  • 給api層引入nuget包,Autofac.Extensions.DependencyInjection,以下圖:
  • 在Program程序入口的CreateHostBuilder中添加autofac服務:
  • 接着在Startup新增ConfigureContainer(ContainerBuilder builder)以下圖:
  • 註釋掉以前在ConfigureService中的注入方法,並在接着在Startup新增ConfigureContainer方法中新增注入,以下圖:
  • 唉~等等,怎麼感受比以前的注入方式還複雜些?別急,咱們改成程序集註入,這樣子它就自動找到對應的父類接口並注入:
  • 測試效果以下圖:
  • 第一種注入程序集的方式,是咱們直接經過反射加載程序集,可是能夠看到,咱們的api層仍是依賴了不少層的,這裏,咱們解耦,刪掉Repository層的引用,只引入對外暴露的接口層:IRepository
  • 這個時候若是你再運行,確定會報錯,由於你api層跟Repository層解耦了,你就無法再用反射Load Repository層的程序集了。接下來咱們改成使用讀取dll文件反射動態地建立實例注入Repository層的程序集:
  • 這個時候,咱們還須要將Repository層的dll輸出目錄更改一下:
  • 運行測試一下:
完美成功,至此,本期的學習和分享就到這裏結束了,多寫,多思考,歡迎留言交流,共同進步,咱們下期再見!
歡迎關注公衆號:dotNET學習天地
一塊兒學習和進步!
相關文章
相關標籤/搜索