AutoFac.NET 控制反轉IOC、依賴注入DI的應用

  • 5204
  • 0

摘要: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(eName);
           eModule.Print(
"");
       }
   }




執行結果如上圖,成功輸出,先E2, 後E1

 

接下來到專案路徑下,將設定檔,改成E1,E2,E1

 

輸出畫面如下

果然,改成先執行E1, 再執行E2,再執行E1了,程式都不用重編譯,大功告成!

 

Rolence  @ Reddor