iT邦幫忙

2022 iThome 鐵人賽

DAY 25
1
自我挑戰組

[Dot Net Core](圖解系列與常用套件)系列 第 25

[Dot Net Core](圖解系列) 25. 優秀套件之運作 - Autofac 運作圖解

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20220925/20144614JAZmBVOSvg.jpg

於.net core 中有內建原生的 DI container,使得可以使用它的 IOC 機制。如果有複雜的情境,可能要於原生的機制上加上一些code,甚至可能不只一些。於是 Autofac 套件可以搭配複雜的情境需求,所以不需再重新加上額外的程式碼,或一堆 IF、else等等。

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 到 容器中。
先觀察註冊時的圖敘述:

(Autofac Container Register diagram - for IOC、DI)
https://ithelp.ithome.com.tw/upload/images/20220925/20144614nU7DtxrclU.jpg

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

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

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

(Autofac ContainerBuilder execute build process)
https://ithelp.ithome.com.tw/upload/images/20220925/20144614jT68qWaCBd.jpg

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

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

(Autofac ContainerBuilder execute build process cont.)
https://ithelp.ithome.com.tw/upload/images/20220925/20144614lbACOLt2qA.jpg

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

  1. 先建立一個新的 Container。

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

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

做好以上準備後,接下來就是於程式中去實現實例,以下圖稍作說明:
(Autofac Container execute Resolve process)
https://ithelp.ithome.com.tw/upload/images/20220925/20144614T2TgX7tReK.jpg

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

如 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)才行,然後請他做菜(委派),菜單要炒鹹蛋, 依照標籤找到鹹鴨蛋與苦瓜等配料,最後 快炒方式煮出苦瓜炒鹹蛋 (用苦瓜裝飾了炒蛋,裝飾者模式)。

之後要怎麼用這些煮好的成品看用途!

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


上一篇
[Dot Net Core](圖解系列) 24.AutoMapper - Implement Mapping object
下一篇
[Dot Net Core](常用套件) 26.套件Autofac於專案中如何快速擴充客製註冊方法
系列文
[Dot Net Core](圖解系列與常用套件)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言