摘要:AutoFac.NET 控制反轉IOC、依賴注入DI的應用
AutoFac.NET 控制反轉IOC、依賴注入DI的應用
需求
我需要一個框架,開發者有多個,每個人負責寫好自已的插件(plugin, 是dll),但都繼承至同一個介面,接著把這多個插件,放到指定目錄下
系統就可以捉這個組件進來用。不用重編譯,只要重新執行即可
系統可以透過設定檔,選擇要使用到的插件來執行(有點類似Commander設計模式),插件執行的先後順序,也由設定檔指定即可
開始
在開始之前,你必須要有一點Autofac的基礎,建議看一下這篇
正式來了~以下會建立四個專案
分別是:一個介面專案,二個Plugin專案,一個測試專案
一、先建一個專案,定義好介面,IEModule
二、建第二個專案,引入第一個介面專案的參考,然後開發Plugin組件,實作IEModule,我叫這個Plugin叫E1,寫好編譯出e1.dll
三、重頭戲,接下來開發我的應用程式
3.1 新建一個專案,Nuget加入以下兩個Autofac
3.2 實作呼叫插件的類別,下文統一叫他插座?…反正紅線就是Plugin (E1)要插入的地方…
(這個插座,只是用來Demo AutoFac可以自動幫你”上插頭”,後面就用不到了)
3.3將剛剛e1.dll組件放在這個專案的bin檔下
3.4、製作設定檔,方便載入Plugin組件,這裏設定剛剛建置的e1.dll名稱
3.5、實作以下程式碼
前面程式碼看不懂沒關係,注意最後一行,呼叫插座的e.Run方法(裏面呼叫E1.Print())
(注意,程式中完全不需要知道E1模組的型別)
3.6、編譯,執行,出現錯誤訊息
'ETL' can be invoked with the available services and parameters:
Cannot resolve parameter 'IEModule e' of constructor 'Void .ctor(ETL.PluginSystem.EModuel.Interface.IEModule)'.
大意是,在ETL類別(插座)的建構式中,找不到可以插進去的IEModule的實作物件
3.7,回頭去看程式碼,原來從網路上Copy Paste沒改到,原文是EndWith,但我的類別是E1類別是E開頭,所以改成StartWith就ok
重新執行
成功輸出hello world , i am e1
四、細看程式碼
在細看之前,先了解做了哪些事~~
1.動態載入e1.dll組件到此專案,不是設計階段加入參考, 只要在config 上設定dll名稱即可
2.組件的檔名,用設定檔設定,方便修改,可以多筆,用逗號隔開
3.Autofac的載入型別流程(RegisterXXX)
4.透過Autofac執行物件(Resolve)
五、指定名稱,使用AutoFac來取得物件的實例
接下來考慮,假設我有多個實作同一個介面的Plugin類別。如何透過指定名稱,使用AutoFac來取得物件的實例
以下是程式碼說明在Autofac註冊時,傳入名稱參數,名稱就是插件的類別名(t.Name)。
紅色的部份
static void Main(string[] args)
{
//==前提,E模組DLL放在系統路徑下;只有一個模組類別,必須E開頭不含其他類別==
//==動態載入E1組件檔設定, 這裏eModuleAssemblyFileNames是e1.dll檔案名稱==
var eModuleAssemblyFileNames = Settings1.Default.EModuleAssemblyFileNames;
//==載入e模組清單==
List assList = new List();
foreach (var ass in eModuleAssemblyFileNames.Split(','))
{
assList.Add( Assembly.LoadFrom(ass));
}
//===autofac 註冊EModule流程,E模組一定啟始名E===
var b = new ContainerBuilder();
//==為了要實作命令設計模式,動態載入的Plugin,要透過以下程式碼給一個名稱,名稱就是類別名稱==
b.RegisterAssemblyTypes(assList.ToArray()).Where(t => t.IsAssignableTo())
.As()
.Named(t => t.Name,typeof(IEModule));
b.RegisterType();
//==實作流程,呼叫ETL類別的Run,成功掛載到E1==
using (var c = b.Build())
{
var e = c.Resolve();
e.Run();
//==試一下透過名稱來建構出E1
var e1 =c.ResolveNamed("E1");
e1.Print("test");
}
}
成功透過類別名稱取得動態Plugin的物件
六、第二個插件出現
現在好辦了,再來實作另一個Plugin,新增另一個專案,輸出e2.dll
寫一個E2類別
一樣,建置,把e2.dll copy到剛剛專案的bin目錄下
更改設定檔,多檔名用逗號隔開
回到測試專案,修改程式碼,看能不能取出E2
執行後
ETL在這裏,建構式會捉最後一個發現實作IEModuel的類別進行,所以第一行顯示E2
第二行,成功取到E1
第三行,成功取到E2
七、實作出只要改設定檔,就可以,先執行E1, 再執行E2...或者E3, E4, E5,主程式永遠不用重編譯的插件框架
新增另一個設定變數
主程式改為如下
//==實作流程,呼叫ETL類別的Run,成功掛載到E1==
using (var c = b.Build())
{
var arrEModulesProcessName = Settings1.Default.EModuleProcess.Split(',');
foreach (var eName in arrEModulesProcessName)
{
var eModule = c.ResolveNamed
eModule.Print(
}
}
執行結果如上圖,成功輸出,先E2, 後E1
接下來到專案路徑下,將設定檔,改成E1,E2,E1
輸出畫面如下
果然,改成先執行E1, 再執行E2,再執行E1了,程式都不用重編譯,大功告成!
Rolence @ Reddor