選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
5 分鐘閱讀時間 (949 個字)

Autofac 套件內部如何達成 IOC 目的 - 概述圖解

logoAutofa_20211103-014146_1

IOC概念幫助我們節省了開發與維護的時間,是非常有感的。以往我們如果自己寫一個幫助自己專案的IOC工具,因為時間上的安排通常會做得比較直接簡約,能夠達成抽換(外部嵌入注入或專案內部物件注入)就算是達成目的,已經不錯了。作法大致上是使用一個static的集合來記錄Type與對應的設定要怎麼產生實例。

而許多套件方面卻又提供了更多的情境來使用。不但產生的實例可以注入到各種架構,還可搭配情境、生命週期、記憶體方面限制等等,使用上讓開發者感覺有非常豐富的支援。其中挑選了 Autofac來使用與探討,原因是:

1. 有足夠不藏私的說明文件.

2. 支援各種架構的使用,套件有持續維護 (包含新的語法來簡化程式).

3. 有多個貢獻者提供擴充與問題修正.

4. 為開源套件,代表使用上也可以自己客製擴充.

當然其它套件也有許多優點,用法也大致上都類似。用法幾乎可以用二個方向來描述 :  第一個是先註冊(Register)。第二個是實現實例(Resolve).

舉個簡單的例子:

//註冊

builder.RegisterType<TextWriterLog>().AsSelf().As<ILog>().SingleInstance(); 

//產生實例

ILog provider = null;
using (var scope = AutofacContainer.builder.BeginLifetimeScope())
{

   if (scope.TryResolve<ILog>(out provider))
   {
    provider.WriteByLog("\n" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") , "ILog write message!");
    }
}

如此即能實現簡單的IOC。

而 Autofac 還能夠讓產生實例的註冊再包含一些設定,如委派的函示、條件的安排,甚至其它情境如 Decorator pattern 與 Adapter pattern 的使用。這時會想,它是如何運作的? 它是怎麼達成的? 

Autofac 會用一個容器將所有資訊存入,並建各種引擎來做對應的事。最終 build 到 容器中。

先觀察註冊時的圖敘述:

在註冊服務這個動作中,最終要產生  RegistrationBuilder 這個物件, 包含了要如何變成實體的資料 ActivatorData 、變成什麼樣的服務 RegistrationData 與 其它協助過濾輔助的資料 RegistrationStyle。

其中的  RegistrationData.DeferredCallback,要存到   ContainerBuilder.configurationCallbacks List集合中,先Hook 執行內容  RegistrationBuilder.RegisterSingleComponent於此,也就是告訴容器這個服務未來要用此方式來產生製造實例的資訊。 

接著看ContainerBuiler註冊時,要產生容器與將資訊放入容器中:

ContainerBuilder為建構容器的阿大引擎。之前透過此大引擎註冊好服務後,這邊開始做建構容器的動作。容器Container內有一個小引擎物件,ComponentRegistry, 它的作用是將ConponentRegistration存到自己的_registrations 與 _serviceinfo 集合內。透過Register方法先將預設的(新產生的必要服務) 轉至 ConponentRegistration 然後存到 自己的_registrations 與 _serviceinfo 集合。

接下來要將註冊好的資訊 (RegistrationBuilder) 將此內容由註冊時所Hook的驅動執行,產生ComponentRegistration存入ComponentRegistry的_registrations 與 _serviceinfo 集合內。

所以我們拉出視野,綜觀ContainerBuilder.build()在做的事:

1. 先建立一個新的 Container。

2. 產生新的ConponentRegistration,裡面放預設必要的基礎服務,放到 Container 的 ComponentRegistry 中的_registrations 與  _serviceinfo 集合

3. 將開發者註冊好的 RegistrationBuilder 轉成 ConponentRegistration 並逐一放到 Container 的 ComponentRegistry 中的_registrations 與 _serviceinfo 集合。


做好以上準備後,接下來就是於程式中去實現實例,以下圖稍作說明:

這邊看到有做遞迴,當一個實例的建構子參數還有需要用到所註冊的服務來產生實例,會接著再去跑一次抓服務資訊來對應並產生實例。所以其實它是可以巢狀地,一層一層的去注入已註冊好的服務。

如   new OutterClass( new MediamClass( new InnerClass()) , 此三個Class 皆可利用註冊服務的方式去resolve出來,而且可以神奇的只下一個指令:

container. Resolve<OutterClass>(); 

就可以幫忙產生好實例了,實在非常簡便,令很多國內外開發者讚嘆。

看到這邊,我相信要真的體會實際運作是有些技巧與想像。也就是仍有一點抽象。所以想了一個例子來做白話描述,讓大家可以想像得出來大致上的運作、作法,或者其實是類似做了什麼:

某人帶了一個籃子(container) , 買了放了各種蛋,並收集了要做哪些與蛋有關的菜單(service) ,貼上標籤,內容為蛋種類、配料與食譜,要準備做出哪個菜單,

標籤註明主要以快炒方式煮這些蛋(Hook RegistrationBuilder.RegisterSingleComponent ),

到了某餐廳(應用程式主體或某 controller action),

要找人做菜 (準備 resolve),

然後到找到某個有證照廚師,把這些資訊給他,請他做菜 (委派),
菜單要炒雞蛋, 依照標籤找到白雞蛋與火腿玉米配料,最後 快炒方式煮出火腿玉米炒蛋 (用火腿玉米裝飾了炒蛋,裝飾者模式)。

找到另一名廚師,這名廚師要先做到洗乾淨手與戴口罩(lambda expression and before prepare event)才行,然後請他做菜(委派),菜單要炒鹹蛋, 依照標籤找到鹹鴨蛋與苦瓜等配料,最後 快炒方式煮出苦瓜炒鹹蛋 (用苦瓜裝飾了炒蛋,裝飾者模式)。

之後要怎麼用這些煮好的食物看用途!


下一篇是介紹如何在此套件擴充客製的功能。

Autofac 套件之如何快速擴充客製註冊方法 - 以變更RegisterDecorator為例
Robot Framework IDE
 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/05/19, 週日

Captcha 圖像