1. 程式人生 > >ASP.NET Core中的依賴注入(2):依賴注入(DI)

ASP.NET Core中的依賴注入(2):依賴注入(DI)

IoC主要體現了這樣一種設計思想:通過將一組通用流程的控制從應用轉移到框架之中以實現對流程的複用,同時採用“好萊塢原則”是應用程式以被動的方式實現對流程的定製。我們可以採用若干設計模式以不同的方式實現IoC,比如我們在上面介紹的模板方法、工廠方法和抽象工廠,接下來我們介紹一種更為有價值的IoC模式,即依賴注入(DI:Dependency Injection,以下簡稱DI)。

目錄
一、由外部容器提供服務物件
二、三種依賴注入方式
    構造器注入
    屬性注入
    方法注入
三、例項演示:建立一個簡易版的DI框架

一、由外部容器提供服務物件

和上面介紹的工廠方法和抽象工廠模式一樣,DI旨在實現針對服務物件的動態提供。具體來說,服務的消費者利用一個獨立的容器(Container)來獲取所需的服務物件,容器自身在提供服務物件的過程中會自動完成依賴的解析與注入。話句話說,由DI容器提供的這個服務物件是一個” 開箱即用”的物件,這個物件自身直接或者間接依賴的物件已經在初始化的工程中被自動注入其中了。

舉個簡單的例子,我們建立一個名為Cat的DI容器類,那麼我們可以通過呼叫具有如下定義的擴充套件方法GetService<T>從某個Cat物件獲取指定型別的服務物件。我之所以將其命名為Cat,源於我們大家都非常熟悉的一個卡通形象“機器貓(哆啦A夢)”。它的那個四次元口袋就是一個理想的DI容器,大熊只需要告訴哆啦A夢相應的需求,它就能從這個口袋中得到相應的法寶。DI容器亦是如此,服務消費者只需要告訴容器所需服務的型別(一般是一個服務介面或者抽象服務類),就能得到與之匹配的服務物件。

   1: public static class CatExtensions
   2:
   3:
     public static T GetService<T>(this Cat cat);
   4: }

對於我們在上一篇演示的MVC框架,我們在前面分別採用不同的設計模式對框架的核心型別MvcEngine進行了改造,現在我們採用DI的方式並利用上述的這個Cat容器按照如下的方式對其進行重新實現,我們會發現MvcEngine變得異常簡潔而清晰。

   1: public class MvcEngine
   2: {
   3:     public Cat Cat { get; private set; }
   4:  
   5:     public MvcEngine(Cat cat)
   6:     {
   7:         this.Cat = cat;
   8:     }
   9:  
  10:     public void Start(Uri address)
  11:     {
  12:         while (true)
  13:         {
  14:             Request request = this.Cat.GetService<Listener>().Listen(address);
  15:             Task.Run(() =>
  16:             {
  17:                 Controller controller = this.Cat.GetService<ControllerActivator>().ActivateController(request);
  18:                 View view = this.Cat.GetService<ControllerExecutor>().ExecuteController(controller);
  19:                 this.Cat.GetService<ViewRenderer>().RenderView(view);
  20:             });
  21:         }
  22:     }
  23: } 

DI體現了一種最為直接的服務消費方式,消費者只需要告訴生產者(DI容器)關於所需服務的抽象描述,後者根據預先註冊的規則提供一個匹配的服務物件。這裡所謂的服務描述主要體現為服務介面或者抽象服務類的型別,當然也可以是包含實現程式碼的具體型別。至於應用程式對由框架控制的流程的定製,則可以通過對DI容器的定製來完成。如果具體的應用程式需要採用上面定義的SingletonControllerActivator以單例的模式來啟用目標Controller,那麼它可以在啟動MvcEngine之前按照如下的形式將SingletonControllerActivator註冊到後者使用的DI容器上。

   1: public class App
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         Cat cat = new Cat().Register<ControllerActivator, SingletonControllerActivator>();
   6:         MvcEngine engine     = new MvcEngine(cat);
   7:         Uri address          = new Uri("http://localhost/mvcapp");
   8:         Engine.Start(address);
   9:     }
  10: }

二、三種依賴注入方式

一項確定的任務往往需要多個物件相互協作共同完成,或者某個物件在完成某項任務的時候需要直接或者間接地依賴其他的物件來完成某些必要的步驟,所以執行時物件之間的依賴關係是由目標任務來決定的,是“恆定不變的”,自然也無所謂“解耦”的說法。但是執行時的物件通過設計時的類來定義,類與類之間耦合則可以通過依賴進行抽象的方式來解除。

從服務使用的角度來講,我們藉助於一個服務介面對消費的服務進行抽象,那麼服務消費程式針對具體服務型別的依賴可以轉移到對服務介面的依賴上。但是在執行時提供給消費者總是一個針對某個具體服務型別的物件。不僅如此,要完成定義在服務介面的操作,這個物件可能需要其他相關物件的參與,換句話說提供的這個服務物件可能具有針對其他物件的依賴。作為服務物件提供者的DI容器,在它向消費者提供服務物件之前會自動將這些依賴的物件注入到該物件之中,這就是DI命名的由來。

3-8如右圖所示,服務消費程式呼叫GetService<IFoo>()方法向DI容器索取一個實現了IFoo介面的某個型別的物件,DI容器會根據預先註冊的型別匹配關係建立一個型別為Foo的物件。此外,Foo物件依賴Bar和Baz物件的參與才能實現定義在服務介面IFoo之中的操作,所以Foo具有了針對Bar和Baz的直接依賴。至於Baz,它又依賴Qux,那麼後者成為了Foo的間接依賴。對於DI容器最終提供的Foo物件,它所直接或者間接依賴的物件Bar、Baz和Qux都會預先被初始化並自動注入到該物件之中。

從程式設計的角度來講,型別中的欄位或者屬性是依賴的一種主要體現形式,如果型別A中具有一個B型別的欄位或者屬性,那麼A就對B產生了依賴。所謂依賴注入,我們可以簡單地理解為一種針對依賴欄位或者屬性的自動化初始化方式。具體來說,我們可以通過三種主要的方式達到這個目的,這就是接下來著重介紹的三種依賴注入方式。

構造器注入

構造器注入就在在建構函式中藉助引數將依賴的物件注入到建立的物件之中。如下面的程式碼片段所示,Foo針對Bar的依賴體現在只讀屬性Bar上,針對該屬性的初始化實現在建構函式中,具體的屬性值由建構函式的傳入的引數提供。當DI容器通過呼叫建構函式建立一個Foo物件之前,需要根據當前註冊的型別匹配關係以及其他相關的注入資訊建立並初始化引數物件。

   1: public class Foo
   2: {
   3:     public IBar Bar{get; private set;}
   4:     public Foo(IBar bar)
   5:     {
   6:         this.Bar = bar;
   7:     }
   8: }

除此之外,構造器注入還體現在對建構函式的選擇上面。如下面的程式碼片段所示,Foo類上面定義了兩個建構函式,DI容器在建立Foo物件之前首選需要選擇一個適合的建構函式。至於目標建構函式如何選擇,不同的DI容器可能有不同的策略,比如可以選擇引數做多或者最少的,或者可以按照如下所示的方式在目標建構函式上標註一個相關的特性(我們在第一個建構函式上標註了一個InjectionAttribute特性)。

   1: public class Foo
   2: {
   3:     public IBar Bar{get; private set;}
   4:     public IBaz Baz {get; private set;}
   5:  
   6:     [Injection]
   7:     public Foo(IBar bar)
   8:     {
   9:         this.Bar = bar;
  10:     }
  11:  
  12:     public Foo(IBar bar, IBaz):this(bar)
  13:     {
  14:         this.Baz = baz;
  15:     }
  16: }

屬性注入

如果依賴直接體現為類的某個屬性,並且該屬性不是隻讀的,我們可以讓DI容器在物件建立之後自動對其進行賦值進而達到依賴自動注入的目的。一般來說,我們在定義這種型別的時候,需要顯式將這樣的屬性標識為需要自動注入的依賴屬性,以區別於該型別的其他普通的屬性。如下面的程式碼片段所示,Foo類中定義了兩個可讀寫的公共屬性Bar和Baz,我們通過標註InjectionAttribute特性的方式將屬性Baz設定為自動注入的依賴屬性。對於由DI容器提供的Foo物件,它的Baz屬性將會自動被初始化。

   1: public class Foo
   2: {
   3:     public IBar Bar{get; set;}
   4:  
   5:     [Injection]
   6:     public IBaz Baz {get; set;}
   7: }

方法注入

體現依賴關係的欄位或者屬性可以通過方法的形式初始化。如下面的程式碼片段所示,Foo針對Bar的依賴體現在只讀屬性上,針對該屬性的初始化實現在Initialize方法中,具體的屬性值由建構函式的傳入的引數提供。我們同樣通過標註特性(InjectionAttribute)的方式將該方法標識為注入方法。DI容器在呼叫建構函式建立一個Foo物件之後,它會自動呼叫這個Initialize方法對只讀屬性Bar進行賦值。在呼叫該方法之前,DI容器會根據預先註冊的型別對映和其他相關的注入資訊初始化該方法的引數。

   1: public class Foo
   2: {
   3:     public IBar Bar{get; private set;}
   4:  
   5:     [Injection]
   6:     public Initialize(IBar bar)
   7:     {
   8:         this.Bar = bar;
   9:     }
  10: }

三、例項演示:建立一個簡易版的DI框架

上面我們對DI容器以及三種典型的依賴注入方式進行了詳細介紹,為了讓讀者朋友們對此具有更加深入的理解,介紹我們通過簡短的程式碼建立一個迷你型的DI容器,即我們上面提到過的Cat。在正式對Cat的設計展開介紹之前,我們先來看看Cat在具體應用程式中的用法。

   1: public interface IFoo {}
   2: public interface IBar {}
   3: public interface IBaz {}
   4: public interface IQux {}
   5:  
   6: public class Foo : IFoo
   7: {
   8:     public IBar Bar { get; private set; }
   9:  
  10:     [Injection]
  11:     public IBaz Baz { get; set; }
  12:  
  13:     public Foo() {}
  14:  
  15:     [Injection]
  16:     public Foo(IBar bar)
  17:     {
  18:         this.Bar = bar;
  19:     }
  20: }
  21:  
  22: public class Bar : IBar {}
  23:  
  24: public class Baz : IBaz
  25: {
  26:     public IQux Qux { get; private set; }
  27:  
            
           

相關推薦

ASP.NET Core依賴注入2依賴注入DI

IoC主要體現了這樣一種設計思想:通過將一組通用流程的控制從應用轉移到框架之中以實現對流程的複用,同時採用“好萊塢原則”是應用程式以被動的方式實現對流程的定製。我們可以採用若干設計模式以不同的方式實現IoC,比如我們在上面介紹的模板方法、工廠方法和抽象工廠,接下來我們介紹一種更為有價值的IoC模式,即依賴注入

ASP.NET Core依賴注入3: 服務的註冊與提供

在採用了依賴注入的應用中,我們總是直接利用DI容器直接獲取所需的服務例項,換句話說,DI容器起到了一個服務提供者的角色,它能夠根據我們提供的服務描述資訊提供一個可用的服務物件。ASP.NET Core中的DI容器體現為一個實現了IServiceProvider介面的物件。 ServiceProvider與

ASP.NET Core依賴注入1控制反轉IoC

ASP.NET Core在啟動以及後續針對每個請求的處理過程中的各個環節都需要相應的元件提供相應的服務,為了方便對這些元件進行定製,ASP.NET通過定義介面的方式對它們進行了“標準化”,我們將這些標準化的元件稱為服務,ASP.NET在內部專門維護了一個DI容器來提供所需的服務。要了解這個DI容器以及現實其中

ASP.NET Core依賴注入4: 建構函式的選擇與服務生命週期管理

ServiceProvider最終提供的服務例項都是根據對應的ServiceDescriptor建立的,對於一個具體的ServiceDescriptor物件來說,如果它的ImplementationInstance和ImplementationFactory屬性均為Null,那麼ServiceProvider

ASP.NET Core依賴注入5ServicePrvider實現揭祕【補充漏掉的細節】

到目前為止,我們定義的ServiceProvider已經實現了基本的服務提供和回收功能,但是依然漏掉了一些必需的細節特性。這些特性包括如何針對IServiceProvider介面提供一個ServiceProvider物件,何建立ServiceScope,以及如何提供一個服務例項的集合。 一、提供一個Serv

ASP.NET Core依賴注入5: ServiceProvider實現揭祕 【總體設計 】

本系列前面的文章我們主要以程式設計的角度對ASP.NET Core的依賴注入系統進行了詳細的介紹,如果讀者朋友們對這些內容具有深刻的理解,我相信你們已經可以正確是使用這些與依賴注入相關的API了。如果你還對這個依賴注入系統底層的實現原理具有好奇心,可以繼續閱讀這一節的內容。 目錄一、ServiceCall

ASP.NET Core依賴注入5: ServiceProvider實現揭祕 【解讀ServiceCallSite 】

通過上一篇的介紹我們應該對實現在ServiceProvider的總體設計有了一個大致的瞭解,但是我們刻意迴避一個重要的話題,即服務例項最終究竟是採用何種方式提供出來的。ServiceProvider最終採用何種方式提供我們所需的服務例項取決於最終選擇了怎樣的ServiceCallSite,而服務註冊是採用的S

ASP.NET Core使用GraphQL - 第三章 依賴注入

ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中介軟體 SOLID原則中的D表示依賴倒置原則。這個原則的內容是: 上層模組不應

ASP.NET Core如影隨形的”依賴注入”[上]: 從兩個不同的ServiceProvider說起

我們一致在說 ASP.NET Core廣泛地使用到了依賴注入,通過前面兩個系列的介紹,相信讀者朋友已經體會到了這一點。由於前面兩章已經涵蓋了依賴注入在管道構建過程中以及管道在處理請求過程的應用,但是內容相對分散和零碎,我們有必要針對這個主題作一個歸納性的介紹。採用依賴注入的服務均由某個ServiceProvi

ASP.NET Core如影隨形的”依賴注入”[下]: 歷數依賴注入的N種玩法

在對ASP.NET Core管道中關於依賴注入的兩個核心物件(ServiceCollection和ServiceProvider)有了足夠的認識之後,我們將關注的目光轉移到程式設計層面。在ASP.NET Core應用中基於依賴注入的程式設計主要涉及到兩個方面,它們分別是將服務註冊到ServiceCollect

Asp.net core依賴注入

原文: Asp.net core中的依賴注入 使用服務 在Asp.net core的Controller中,可以通過如下兩種方式獲取系統注入的服務: 建構函式 可以直接在建構函式中傳入所依賴的服務,這是非常常見的DI注入方式。     public 

體驗 ASP.NET Core 的多語言支持Localization

lan expander -c blank 根據 body esp doc input 首先在 Startup 的 ConfigureServices 中添加 AddLocalization 與 AddViewLocalization 以及配置 RequestLocaliz

7學習筆記 ASP.NET CORE微服務 Micro-Service ---- 利用Polly+AOP+依賴註入封裝的降級框架

tostring methods summary bstr 判斷 KS foreach public tde 創建簡單的熔斷降級框架 要達到的目標是: 參與降級的方法參數要一樣,當HelloAsync執行出錯的時候執行HelloFallBackAsync方法。 pu

ASP.NET Core 如何給中間件傳參數轉載

inject its mes str project dsc format blank sam Passing Parameters to Middleware in ASP.NET Core 2.0 Problem How do you pass paramet

ASP.NET Core 的 WebSocket 支持轉自MSDN

ocs 接收 緩沖 任務 ica uget 本地服務器 tcp msdn 本文介紹 ASP.NET Core 中 WebSocket 的入門方法。 WebSocket (RFC 6455) 是一個協議,支持通過 TCP 連接建立持久的雙向信道。 它用於從快速實時通信中獲益的

Core使用Hangfire 在Asp.Net Core使用DI的方式使用Hangfire構建後臺執行指令碼 解決 ASP.NET Core Hangfire 未授權401 Unauthorized

    之前使用Quartz.Net,後來發現hangfire對Core的繼承更加的好,而且自帶管理後臺,這就比前者好用太多了。 安裝註冊 安裝 PM> Install-Package Hangfire Startup.cs,在ConfigureServices方法中添加註冊:

ASP.NET Core使用IOC三部曲(二.採用Autofac來替換IOC容器,並實現屬性注入)

https://www.cnblogs.com/GuZhenYin/p/8301500.html     上一篇我們說過ASP.NET Core中自帶的IOC容器是屬於輕量級的,功能並不是很多,只是提供了基礎功能而已.. 所以今天我們主要講講如何採用Autofac

ASP.NET Core 的 WebSocket 支援轉自MSDN

本文介紹 ASP.NET Core 中 WebSocket 的入門方法。 WebSocket (RFC 6455) 是一個協議,支援通過 TCP 連線建立持久的雙向通道。 它用於從快速實時通訊中獲益的應用,如聊天、儀表板和遊戲應用。 如果不明白什麼是WebSocket可以參考這篇文章   系統

ASP.NET CORE 使用 SESSION 轉載

Session 是儲存使用者和 Web 應用的會話狀態的一種方法,ASP.NET Core 提供了一個用於管理會話狀態的中介軟體。在本文中我將會簡單介紹一下 ASP.NET Core 中的 Session 的使用方法。     安裝配置 Session nuget 新增引用 M

.NET Core的一個介面多種實現的依賴注入與動態選擇看這篇就夠了

最近有個需求就是一個抽象倉儲層介面方法需要SqlServer以及Oracle兩種實現方式,為了靈活我在依賴注入的時候把這兩種實現都給注入進了依賴注入容器中,但是在服務呼叫的時候總是獲取到最後注入的那個方法的實現,這時候就在想能不能實現動態的選擇使用哪種實現呢?如果可以的話那麼我只需要在配置檔案中進行相應的配置