ASP.NET Core中的依賴註入(1):控制反轉(IoC)

来源:http://www.cnblogs.com/artech/archive/2016/04/04/asp-net-core-di-ioc.html
-Advertisement-
Play Games

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


ASP.NET Core在啟動以及後續針對每個請求的處理過程中的各個環節都需要相應的組件提供相應的服務,為了方便對這些組件進行定製,ASP.NET通過定義介面的方式對它們進行了“標準化”,我們將這些標準化的組件稱為服務,ASP.NET在內部專門維護了一個DI容器來提供所需的服務。要瞭解這個DI容器以及現實其中的服務提供機制,我們先得知道什麼是DI(Dependence Injection),而一旦我們提到DI,又不得不說IoC(Inverse of Control)。

一、流程式控制制的反轉

我聽到很多人將IoC說成是一種“面向對象的設計模式”,但在我個人看來IoC不能算作一種“設計模式”,其自身也與“面向對象”沒有直接的關係。很多人之所以不能很準確地理解IoC源於他們忽略了一個最根本的東西,那就是IoC這個短語。換句話說,很多人之所以對IoC產生了諸多誤解是因為他們忽略了IoC的定義。

IoC的全名Inverse of Control,翻譯成中文就是“控制反轉”或者“控制倒置”。控制反轉也好,控制倒置也罷,它體現的意思是控制權的轉移,即原來控制權在A手中,現在需要B來接管。那麼具體對於軟體設計來說,IoC所謂的控制權的轉移具有怎樣的體現呢?要回答這個問題,就需要先瞭解IoC的C(Control)究竟指的是怎樣一種控制,在我看來這裡所謂的控制更多地體現為一種“流程的控制”。

我們通過一個具體事例來說明傳統的設計在採用了IoC之後針對流程的控制是如何實現反轉的。比如說我們現在設計一個針對Web的MVC類庫,不妨將其命名為MvcLib。MvcLib提供瞭如下所示的API幫助我們完成整個HTTP請求流程中的主要任務。具體來說,ListenAndReceiveRequest方法啟動一個監聽器綁定到指定的地址進行請求的監聽,接收到的請求通過一個Request對象返回。ActivateController方法根據接收到的請求解析並激活請求的目標Controller。ExecuteContrller方法執行激活的Controller並返回一個表示視圖的View對象。RenderView最終將View對象轉換成HTML並作為當前請求響應的內容。

   1: public static class MvcLib
   2: {
   3:     public static Request ListenAndReceiveRequest(Uri address);
   4:     public static Controller ActivateController (Request request);
   5:     public static View ExecuteContrller(Controller controller);
   6:     public static void RenderView(View view);
   7: }
   8:  

現在我們在這個MvcLib的基礎上創建一個真正的MVC應用,那麼除了按照MvcLib的規範自定義具體的Controller和View之外,我們還需要自行控制從請求的監聽與接收、Controller的激活與執行以及View的最終呈現在內的整個流程,這樣一個執行流程反映在如下所示的代碼中。

   1: public class App
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         Uri address = new Uri("http://localhost/mvcapp");
   6:         while (true)
   7:             {
   8:                 Request request = MvcLib.ListenAndReceiveRequest(address);
   9:                 Task.Run(()=> ProcessRequest(request));
  10:             }
  11:         }
  12:  
  13:     private static void ProcessRequest(Request request)
  14:     {
  15:         Controller controller = MvcLib.ActiveController(request);
  16:         View view = MvcLib.ExecuteContrller(controller);
  17:         MvcLib.RenderView(view);
  18:     }
  19: }

這個例子體現了右如圖所示的流程式控制制方式,即我們設計的類庫(MvcLib)僅僅通過API的形式提供某種單一功能的實現,作為類庫消費者的應用程式(App)則需要自行編排整個工作流程。如果從重用的角度來講,這裡被重用的僅限於實現某個環節單一功能的代碼,編排整個工作流程的代碼並沒有得到重用。

3-1

當我們構建一個應用的時候,我們不僅僅是需要一個能夠提供API的類庫,實際上更理想的形式是直接在一個現有的框架上構架我們的應用。類庫(Library)和框架(Framework)的不同之處在於,前者往往只是提供實現某種單一功能的API,而後者則針對一個目標任務對這些單一功能進行編排形成一個完整的流程,這個流程在一個引擎的驅動下被執行

在我們上面演示MvcLib來說,使用它的應用程式需要自行控制整個HTTP請求的處理流程,實際上這是一個很“泛化”的工作流程,幾乎所有的MVC應用均採用這樣的流程監聽、接收請求並最終對請求予以響應。如果我們將這個流程實現在一個MVC框架之中,由它構建的所有MVC應用就可以直接使用這個請求處理流程,而並不需要自行重覆實現它。

3-2現在我們將MvcLib從類庫改造成一個框架,並姑且將其稱為MvcFrame。如左圖所示,MvcFrame的核心是一個被稱為MvcEngine的執行引擎,它驅動一個編排好的工作流對HTTP請求進行一致性處理。如果我們利用MvcFrame構建一個具體的MVC應用,除了根據我們的業務需求定義相應的Controller和View之外,我們只需要初始化這個引擎並直接啟動它即可。如果你曾經開發過ASP.NET MVC應用,你會發現ASP.NET MVC就是這麼一個框架。3-3

有了上面演示的這個例子作為鋪墊,我們應該很容易理解IoC所謂的控制反轉了。總的來說,IoC是我們設計框架所採用的設計思想,所謂的控制反轉即是按照如右圖所示的方式將原來實現在應用程式流程式控制制轉移到框架中。被轉移的是一個泛化的可重用的處理流程,所以IoC符合軟體設計一個基本的原則,即重用性。

二、對流程的定製

3-4我們採用IoC實現了流程式控制制從應用程式向框架自身的反轉,但是這個被反轉的僅僅是一個泛化的流程,任何一個具體的應用都可能需要對組成該流程的某些環節進行定製。如左圖所示,我們將一個泛化的工作流程(A=>B=>C)被定義在框架之中,建立在該框架的兩個應用需要對組成這個流程的某些環節進行定製。比如步驟A和C可以被App1重用,但是步驟B卻需要被定製(B1),App2則重用步驟A和B,但是需要按照自己的方式(C2)處理步驟C。

IoC將對流程的控制從應用程式轉移到框架之中,框架利用一個引擎驅動整個流程的執行。換句話說,應用程式無需關心該工作流程的細節,它只需要啟動這個引擎即可。但是這個引擎一旦被啟動,框架就會完全按照預先編排好的流程進行工作,如果應用程式希望整個流程按照自己希望的方式被執行,針對流程的定製一般在發生在啟動引擎之前。

一般來說,框架會以相應的形式提供一系列的擴展點,應用程式則通過定義擴展的方式實現對流程某個環節的定製。在引擎被啟動之前,應用程式將所需的擴展註冊到框架之中。一旦引擎被正常啟動,這些註冊的擴展會自動參與到整個流程的執行過程中。

雖然應用程式是框架引擎的啟動著,但是一旦引擎被啟動之後它就喪失了對流程的控制,應用程式對流程的定製不是在執行過程中對框架的干預來完成的,而只需要在流程執行之前就將定製的部分準備好,框架自身在執行過程中會智能地選擇它們。從這個意義上講,IoC對流程的定製遵循著這樣一個原則,即“Don't call us, we'll call you!”,它被稱為好萊塢原則

綜上所述,IoC一方面通過流程式控制制從應用程式向框架的反轉實現了針對流程自身的重用,另一方面採用“好萊塢原則”使得這個被重用的流程可能自由地被定製,這兩個因素實際上決定了框架自身的價值。重用讓框架不僅僅是為應用程式提供實現單一功能的API,而是提供一整套可執行的解決方案;可定製則使我們可以為不同的應用程式對框架進行定製,這無疑讓框架可以使用到更多的應用之中。

三、 IoC模式

正如我們在上面提到過的,很多人將IoC理解為一種“面向對象的設計模式”,實際上IoC自身不僅與面向對象沒有必然的聯繫,它也算不上是一種設計模式。一般來講,設計模式提供了一種解決某種具體問題的方案,但是IoC既沒有一個針對性的問題領域,其自身沒有提供一種可實施的解決方案,所以我更加傾向於將IoC視為一種設計原則,實際上很多我們熟悉的設計模式背後採用了IoC原則。

模板方法(Template Method)

提到IoC,很多人首先想到的是DI,但是在我看來與IoC聯繫得最為緊密的則是另一種被稱為“模板方法”的設計模式。模板方法模式與IoC的意圖可以說完全一致,該模式主張將一個可復用的工作流程或者由多個步驟組成的演算法定義成模板方法,組成這個流程或者演算法的步驟則實現在相應的虛方法之中,模板方法根據流程先後調用這些虛方法。所有這些方法均定義在同一個類中,我們可以通過派生該類並重寫相應的虛方法達到對流程定製的目的。

對於上面我們演示的這個MVC的例子,我們可以將整個請求處理流程實現在如下一個MvcEngine類中,請求的監聽與接收、目標Controller的激活與執行以及View的呈現則分別定義在四個受保護的虛方法中,模板方法Start根據預定義的請求處理流程先後調用這四個方法。

   1: public class MvcEngine
   2: {
   3:     public void Start(Uri address)
   4:     {
   5:         while (true)
   6:         {
   7:             Request request = this.OnListenAndReceiveRequest(address);
   8:             Task.Run(() =>
   9:             {
  10:                 Controller controller = this.OnActivateController(request);
  11:                 View       view       = this.OnExecuteContrller(controller);
  12:                 this.OnRenderView(view);
  13:             });
  14:         }
  15:     }
  16:     protected virtual Request OnListenAndReceiveRequest(Uri address) ;
  17:     protected virtual Controller OnActivateController(Request request) ;
  18:     protected virtual View OnExecuteContrller(Controller controller) ;        
  19:     protected virtual void OnRenderView(View view) ;
  20: }

對於具體的應用來說,如果定義在MvcEngine針對請求的處理方式完全符合它的要求,它只需要創建這個一個MvcEngine對象,然後指定一個對應的基地址調用模板方法Start開啟這個MVC引擎即可。如果該MVC引擎處理請求的某個環節不能滿足它的要求,它可以創建MvcEngine的派生類,並重寫實現該環節的相應虛方法即可。比如說定義在某個應用程式中的Controller都是無狀態的,它希望採用單例(Singleton)的方式重用已經激活的Controller以提高性能,那麼它就可以按照如下的方式創建一個自定義的FoobarMvcEngine並按照自己的方式重寫OnActivateController方法既可。

   1: public class FoobarMvcEngine : MvcEngine
   2: {
   3:     protected override View OnExecuteContrller(Controller controller)
   4:     {
   5:         <<省略實現>>
   6:     }
   7: }

模板方法如果結合“事件註冊”往往可以使應用程式對流程的定製變得更加自由。如下麵的代碼片段所示,我們為Controller的激活與執行以及View的呈現定義了六個事件,它們分別在這個三個環節開始之前和結束之後被觸發。這麼一個MvcEngine可以直接被使用,應用程式只需要註冊相應的事件完成對請求處理流程的定製。

   1: public class MvcEngine
   2: {
   3:     //其他成員
   4:     protected virtual Controller OnActivateController(Request request) ;
   5:     protected virtual View OnExecuteContrller(Controller controller) ;        
   6:     protected virtual void OnRenderView(View view) ;
   7:  
   8:     public EventHandler<ControllerActivationEventArgs> ControllerActivating;
   9:     public EventHandler<ControllerActivationEventArgs> ControllerActivated;
  10:     public EventHandler<ControllerExecutionEventArgs> ControllerExecuting;
  11:     public EventHandler<ControllerExecutionEventArgs> ControllerExecuted;
  12:     public EventHandler<ViewRenderEventArgs> ViewRendering;
  13:     public EventHandler<ViewRenderEventArgs> ViewRendered;
  14: }

工廠方法(Factory Method)

對於一個複雜的流程來說,我們傾向於將組成該流程的各個環節實現在相應獨立的組件之中,那麼針對流程的定製就可以通過提供不同組件的形式來實現。我們知道23種設計模式之中有一種重要的類型,那就是“創建型模式”,比如常用的“工廠方法”和“抽象工廠”,那麼IoC所體現的針對流程的共用與定製可以通過它們來完成。

3-5所謂的工廠方法,說白了就是在某個類中用於提供依賴對象的方法,這個方法可以是一個單純的虛方法,也可以是具有預設實現的虛方法,至於方法聲明的返回類型,可以是一個介面或者抽象類,也可以是未被封閉(Sealed)的具體類型。作為它的派生類型,它可以實現或者重寫工廠方法以提供所需的具體對象。

同樣以我們的MVC框架為例,我們讓獨立的對象來完成組成整個請求處理流程的四個核心環節。具體來說,我們分別定義了四個核心的類型(Listener、ControllerActivator、ControllerExecutor和ViewGenderer)來分別負責請求監聽與接收、Controller的激活、Controller的執行以及View的呈現。這四個對象按照如右圖所示的順序相互協作完成對請求的處理。

如下所示的Listener、ControllerActivator、ControllerExecutor和ViewGenderer這四個類型的簡單定義。我們沒有將它們定義成單純的抽象類或者介面,而是未被封閉可以被繼承的一般類型,定義其中的虛方法具有預設的實現。只有這些預設的實現方式無法滿足應用程式具體需求的時候,我們才需要定義相應的派生類。

   1: public class Listener
   2: {
   3:     public virtual Request Listen(Uri address) ;
   4: }
   5:  
   6: public class ControllerActivator
   7: {
   8:     public virtual Controller ActivateController(Request request) ;
   9: }
  10:  
  11: public class ControllerExecutor
  12: {
  13:     public virtual View ExecuteController(Controller controller) ;
  14: }
  15:  
  16: public class ViewRenderer
  17: {
  18:     public virtual void RenderView(View view) ;
	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • epol學習筆記 l epoll的相關係統調用 epoll_create() epoll_ctl() epoll_wait() l int epoll_create(int size); 創建一個epoll的句柄。 l int epoll_ctl(int epfd, int op, int fd, ...
  • 原文鏈接:http://www.orlion.ga/1015/ 一、進程 每個進程在內核中都有一個進程式控制制塊(PCB)來維護進程相關的信息,linux內核的進程式控制制塊是task_struct結構體,其中有: 進程id。系統中每個進程有一個唯一的id,在C語言中用pid_t類型表示,是一個非負正是 進 ...
  • 原文鏈接:http://www.orlion.ga/1008/ linux在不同的文件系統之上做了一個抽象層,使得文件、目錄、讀寫訪問等概念都成為抽象層概念,這個抽象層被稱為虛擬文件系統(VFS)。 linux內核的VFS子系統如下: 每個進程在PCB(Process Control Block)中 ...
  • 5.2 使用select,poll // CPU占用率低,適用於很多簡單場合 參考:UNIX環境高級編程 I/O多路轉接 監測多個文件,只要有某一個文件可讀/可寫/異常或超時,即返回 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_... ...
  • 之前在網上看過很多對這方面的講解,但個人覺得看下來過於 "深奧",不容易理解,所以想用更簡單的方式進行闡述,便於理解。 本次我們重點分析用戶請求到頁面呈現過程中Web伺服器的處理過程。我們從ASP.NET站點的一個頁面請求開始說起,先看下麵對於某個請求的簡單執行模型 (註意這是對asp.net站點I ...
  • 模擬一個方法 Controller: namespace WebApplication1.Controllers { public class TestController : Controller { public TestController(IService service) { this.S ...
  • 一、引言 在前一篇文章已經詳細介紹了SignalR了,並且簡單介紹它在Asp.net MVC 和WPF中的應用。在上篇博文介紹的都是群發消息的實現,然而,對於SignalR是為了實時聊天而生的,自然少了不像QQ一樣的端對端的聊天了。本篇博文將介紹如何使用SignalR來實現類似QQ聊天的功能。 二、 ...
  • 作者:[美]Adam Freeman 來源:《精通ASP.NET MVC 4》 本文將繼續構建 SportsStore 示例應用程式。在上一章中,添加了對購物車的基本支持,現在打算改善並完成其功能。 1.使用模型綁定 MVC 框架使用了一個叫作“模型綁定”的系統,以便通過 HTTP 請求來創建一些 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...