使用 Autofac 进行依赖注入
?先說下為什么翻譯這篇文章,既定的方向是架構,然后為了學習架構就去學習一些架構模式、設計思想。
? ? ?突然有一天發現依賴注入這種技能。為了使得架構可測試、易維護、可擴展,需要架構設計為松耦合類型,簡單的說也就是解耦。為了解耦前面的人提出各種理論,主要思想是控制反轉,而現在主流的主要是兩個:依賴注入、服務定位(有篇英文文章特意討論這種模式,最終的結論是否定的,乍看了一眼,沒看懂)
? ? 有了某個思想便可以在程序中體現,用多了,人們就去對它進行封裝,普通的人封裝的大概就自己用,高人封裝了便成了組件。現在基本上都在弄 .net ,所以說的技術也基本上是這一塊的。.net 的 IOC 框架有?Autofac、Castle Windsor、Unity、Spring.NET、StructureMap、Ninject。
? ? 有這么多框架為什么選 Autofac,我在這個地方只是學習這種 IOC 思想的框架,并沒有真實的用在生產中。Autofac 各方面的的性能都比較中庸,所以用它作為 IOC 入門應該是比較合適的,這樣也就導致這篇文章適合初學者閱讀。
? ? 接下來就要具體說下翻譯的原文,文章對于 Autofac 在 IOC 思想的實現和 Autofac 如何使用做了十分透徹的說明,但是本人水平有限,一些地方沒有翻譯對,甚至翻譯錯了,這些我特意指明,省的坑了各位,如果發現不對直接參考原文,但也請告知我,以便改正。
? ? 原文地址:http://www.codeproject.com/Articles/25380/Dependency-Injection-with-Autofac
----------------------------------------------------------------------------------------------------------------------------------------------
Dependency Injection with Autofac
使用 Autofac 進行依賴注入
目錄
Introduction-介紹
Autofac 是發布在 Google Code 上的依賴注入或控制反轉的開源容器。
Autofac 與許多類似技術不同的是它堅持以近乎 C# 的材質
等等
The Example Application-應用程序示例
這個程序是一個控制臺程序,它檢查一個備忘錄列表,每一個備忘錄有一個過期日期,程序向用戶提醒哪些過期的備忘錄。
Checking for Overdue Memos-檢查過期備忘錄
以下幾點作為使用依賴注入的直接原因:
1 以參數接收所有依賴的對象
2 獨立的持久化-IQueryable 對象可能是數據庫表、一個結構文件,再或者內存集合。(集合公布)
3 提醒用戶的形式是獨立的。(以接口控制)
這些舉措使得這個類容易測試,可配置的并且是可維護的。
Notifying the User-提醒用戶
Data Storage-數據倉儲
IQueryable 接口在 dotnet 3.5 中引入,它適合作為備忘錄的數據源。因為它可以同時用于內存對象和關系型數據庫的查詢。
Wiring Up Components-連接組件
應用程序最基礎的結構如下:
?
本文主要關注的是 MemoCHecker 如何獲取與它關聯的 notifier 和 memo 服務,以及每個被依賴的對象如何獲取他們的實體。【有個反轉的概念】
Dependency Injection by Hand-手動依賴
更重要的是,它很難去切換不同的實現服務;比如可能用 EmailNotifier 替換 printing notifier,但是原有的會有依賴,或許不同來源于這些 PrintingNotifier【不理解】,但也有可能是與依賴的其它組件有交互。(這也可能是這些組合現成的問題,而它本身就值得寫文說明)
Autofac 和其它的依賴組件通過在配置時“扁平化”深度網狀對象圖。【待譯】
Dependency Injection with a Container-容器依賴
當使用 Autofac,訪問 MemoChecker 是分離的來源于以下創建方式:
Container.Resolve() 執行請求一個 MemoChecker 的實例,它負責實例的實例化和準備使用。那么,容器是如何工作的、如何創建 MemoChecker 實例。
Component Registrations-注冊組件
依賴注入容器是一個把服務映射到組件的集合。在這種情形下,服務是用來識別某一特定的功能,它可以是一個文本標簽,但其通常是某個接口。
注冊器在系統內部捕獲組件動態的行為。其最受關注點是如何創建組件的實例。
Autofac 能夠接受創建組件的注冊方式有:表達式、提供實例和基于 System.Type 反射。【待譯】
Registering a Component Create with Expression-使用表達式注冊組件
下面就是為 MemoChecker 組件設置一個注冊器:
每一條 Register() 語句僅只處理那些處于對象圖頂端的且其直接關聯到依賴對象上。
C=> new MemoChecker(…) 將被用于容器創建 MemoChecker 組件。
每個 MemoChecker 依賴于額外的兩個服務:IQueryable<Memo> 和 IMemoDueNotifier 。這些服務通過容器調用 Resolve() 方法在 lambda 表達式內檢索到,容器是通過參數 c 傳遞過來的。
上面的注冊器并沒有對 IQueryabl<Meno> 和 IMemoDueNotifier 的實現進行任何說明。這兩個服務的配置依賴關系同 MemoChecker 的配置類似。
上面的表達式將被提供于 Register() 當其返回 MemoChecker 類型,于是,Autofac 會將其作為這個注冊器的默認服務,除非有另外的更為準確的 As() 方法,下面這個 As() 方法包含更為明確的目的:
無論哪種方式,某個 MemoChecker 實例的請求都將是調用我們的表達式后的某一結果。
Autofac 不會在組件注冊時執行表達式,相反,它會等到 Resolve<MemoChecker>() 調用時執行表達式。這一點很關鍵,因為它除去了組件注冊的順序的依賴。【依賴關系不受注冊順序影響】
Registering a Component Instance-使用實例注冊組件
IQueryable<Memo> 服務由存在的 memos 實例提供,PrintingMemoNotifier 類最終指向 TextWriter 的實例 Console.Out 。
Memos 和 Console.Out 都以已經創建的實例提供給容器。(對于 ExternallyOwned() 的解釋,請參考 Deterministic Disposal)
Registering a Component with its Implementation Type-使用實現類型注冊組件
Autofac 也可以用其它容器創建組件的方式-反射來創建組件。(更多這個場景的優化伴隨著 MSIL-generation)
這種方式的含義是說你可以告訴 Autofac 有關于提供服務的類型,并且容器將會使用最合適的構造函數,以及根據其它可用的服務來選擇參數。
MemoChecker 注冊可以被下面的形式替換:
一般說來,最常見的做法是使用自動連接去注冊某一批次的組件。【待譯】
這種方式使得大量的組件可以使用但沒有繁重的注冊每一個組件的消耗,并且你需要清楚的思考這種情形。Autofac 提供如下快捷的方式批次注冊組件:
自動連接在以程序 XML 配置文件注冊組件時也特別實用。
Completing the Example-完整示例
在請求 MemoChecker 服務之前程序創建注冊組件如下所示:
在上面的配置代碼中沒有嵌套顯示了“扁平”的依賴結構而這些由容器提供。
這似乎很難看到上面這種注冊方式比手動的例子更為之間的對象結構,但請再次回憶起,這個示例比平常的系統擁有更少的組件。
最為重要的區別是現在每個組件的配置都獨立于所有其他的組件。伴隨著更多的組件添加到系統中,他們可以被理解為純凈的按照這些服務所暴露的河這些服務所需要的。這是一種有效的控制架構復雜化的手段。
Deterministic Disposal-指定清理
IDisposable 兼具祝福和詛咒。組件有一致的方式去同其應當被清理交互這是好的。不幸的是,哪個組件在什么時候應當被清理并不總是很容易確定。
這個問題由于允許相同的服務有不同的實現而變得糟糕。在這個示例中,IMemoDueNotifier 有許多不同的實現可能被部署。其中的一些將有工廠創建,一些將會是單例的形式,一些將會被清理,還有一些將不會被清理。
組件作為一個通告者是沒有辦法決定它們應當嘗試把它丟給 IDisposable 和調用 Dispose() 或者是不這樣做。各種記錄的結果是導致易錯的河繁瑣的。
Autofac 使用通過容器跟蹤創建的所有可以清理對象方式解決這個問題。記錄的示例如下:
容器在一個 using 程序塊中,因為它擁有所有它創建的組件的所有權,并且清理它們當容器被清理的時候。
這一點很重要因為它真正實現了從配置關注分離的精神【待譯】,MemoChecker 服務可以在任何需要它的時候使用,甚至是以另外被依賴的組件角色被直接創建,不用擔心它們是否應當被清理。
伴隨著這個帶來的內心的平靜,你甚至不用來回讀示例程序來發現任何需要實現 IDisposable (實際是沒有)的類,因為你可以依賴容器去做正確的事情。
Disabling Disposal
記住 ExternallyOwned() 子句在上面完整的示例中添加到 Console.Out 的注冊上。這是合意的因為 Console.Out 是可清理的,但這個時候容器不應當去清理它。
Fine-Grained Control of Component Lifetimes-精細控制組件生命周期
容器將會正常的存在于應用程序執行期間,并且在相同應用程序長生命周期內清理它是一個很好的方式去釋放組件所擁有的資源。大多數不平凡的程序也應釋放資源在一些其它的時候,如:Http 請求完成、工作線程退出或者一個用戶會話結束。
Autofac 使用嵌套的生命周期域幫助你管理這些生命周期,代碼如下:
生命周期管理是通過注冊組件實例映射到生命周期域實現的。
Component Lifetime-組件生命周期
Autofac 允許你指定一個組件多少實例可以駐留以及它們將如何在其它組件間共享。
控制組件獨立的定義作用域是一個非常重要的改進對于傳統方法使用靜態 Instance 屬性來定義單例。這個區別在于對象是什么和如何使用對象。【待譯】
使用 Autofac 最常用生命周期設置如下:
單例
每一依賴一個實例
每一生命作用域一個實例
Single Instance-單例
單例生命周期,它將是大多數組件只有一個實例在容器中的選擇,并且實例會隨著創建它的容器清理時被清理。
一個組件可以使用 SingleInstance() 修飾配置為這種生命周期,如下所示:
每次這樣的組件從容器請求時,都會返回相同的實例:
Instance Per Dependency-每一依賴一個實例
當組件注冊時沒有特意指定生命周期,將會默認 instance-per-dependency 生命周期。每次從容器獲取這樣的組件時,都會返回新建的實例:
某個使用這種生命周期的組件將會跟隨著組件被創建的生命周期而被清理。如果一個 per-dependency 組件被請求是去構造一個 single-instance 組件,對于這種例子,那么 per-denpendency 組件將會隨著 single-instance 組件對于容器的生命周期一直存在。
Instance per Lifetime Scope-每一生命周期作用域一個實例
這種方式滿足每一線程、每一請求或者每一事務組件的生命周期的靈活需求。簡單創建一個生命作用域可以生存在必須的生命周期持續的周期。【待譯】來自相同的作用域請求將會得到相同的實例,同時來自不同作用域的請求將會得到不同的實例。
Using Scope to Control Visibility-使用作用域去控制組件依賴的可見性
組件間的依賴僅只在其滿足其它的組件在其相同的作用域或者在其外部(父)作用域才能建立。這樣確保組件間的依賴在其沒有建立前被處理掉。【待譯】如果 application、session 和Request 有著嵌套的需求,那么可能會按照如下的方式創建:
在這個示例中請知道,appContainer 將會創建許多子 sessionLifetime,并且每個 session 同appContainer 一樣會有許多子 controller。【待譯】
在這個場景,允許依賴的方向是 Request -> session -> application。用于處理用戶請求的組件可以引用任何其他的組件。但是依賴關系在相反的方向上是不被允許的,所以,對于這個情況,應用程序級別的 single-instance 組件將不會連接到指定單例用戶 session 的組件。【待譯】
在這樣的層次結構中,Autofac 總是服務于 shortest-lived 生命周期的組件請求。這將會平常的請求生命周期。【待譯】Single-instance 組件將會駐留在應用程序級別,來將組件的生命周期關聯到 session 級別,詳情請參考 wiki。【待譯】
Autofac 的作用域模式是靈活和有效的。作用域和嵌套生命周期間的關系使得注冊龐大的依賴成員成為可能。
Autofac in Application-在程序中使用 Autofac
依賴注入是一種極其強大的結構控制機制,但是想要獲取這些優勢,系統的組件通過容器成為其它組件能用的占比十分重要。
到目前為止 Autofac 所描述的特征都是被設計為獲取存在的,當“老的簡樸的” .Net 組件添加到容器中,不需要修改或適配代碼。
Expressive Registrations-顯示注冊
使用表達式來向容器注冊組件在使用 Autofac 的應用程序中。一些示例場景說明這些由 Autofac 實現的:
已經存在的工廠方法可以用表達式的形式使用:
已經存在需要在第一次訪問時加載的單例可以使用表達式注冊,并且它的加載是延遲的:
傳遞給組件的參數可以使任何的來源:
某個類型的實現甚至可以根據參數來決定:
Simplified Integration-簡單集成
集成,在這里的意思是使得已存在的類庫服務和程序組件通過容器使他們可以使用。
Autofac 支持一些典型場景的集成比如在 ASP.Net 應用程序中使用的那樣;然而,Autofac 模型的靈活性產生了大量的繁瑣的集成工作,因而最好的方式是把這些留給程序設計者在他們程序最合適的地方實現。
基于表達式的注冊和指定的清理以及延遲加載組件的方案,可以產生令人驚奇的效果當它們集成在一起時:
這是一個 WCF client 集成自 Autofac 網站的示例。ITrackListing 和 IChannelFactory<ITrackkListing> 這兩個關鍵的服務,WCF 管道對于這些通用的 bit 流很容易基于表達式進行注冊。【待譯】
一些要點如下:
Channel 工廠只有在其需要的時候創建,但是一旦創建,它將保留以在每一次 ITrackListing 請求時重用。
ITrackListing 不繼承自 IDisposable,但在 WCF 中,客戶端服務代理以這種方式創建需要聯系到 IDisposable 并且清理它。使用 ITRackListing 接口,使得仍然不知道其實現細節。
終端信息可以來自任何地方-任意服務、數據庫或者某個配置文件。
除了使用基本的 Register() 方法沒有其他概念引入。
這小節向你展示 Autofac 是如何工作的,使得你集中于實現你的應用程序,而不是擴展或驚奇 DI 容器的復雜性。
Where to Next?
我希望這篇文章用來說明來學習如何使用 Autofac 的各種要點,接下來的步驟可能是:
下載源代碼
在 Wiki 上文章
在論壇里介紹你自己
相關文章:?
為ASP.NET MVC程序集成Autofac
基于DDD的現代ASP.NET開發框架--ABP系列之6、ABP依賴注入
原文地址:http://www.cnblogs.com/Ricky81317/p/5604690.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的使用 Autofac 进行依赖注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Angular 2与TypeScript
- 下一篇: webpack 前端构建