爲何要使用MEF 架構
在商業應用軟件開發過程當中,對於各個軟件項目,都須要創建相應的系統框架,爲了更好的規範系統的開發,提升生產效率,應該在公司級別制定相應的API標準。這些API標準將站在系統架構層次,以一樣一個核心框架構建出不一樣的商業應用。 框架
對於各個商業應用中存在花樣繁多的需求,同時又存在一些公用的模塊,爲了將這些可變的和相對穩定的功能模塊有機的整合在一個系統框架下,那麼就須要實現系統框架的可自定義插件開發。目前在MEF以前,業界也在大量的使用如 Castle Windsor、Structure Map、Spring.Net 以及Unity等依賴注入方式實現插件開發。而這些體系在.net平臺中應用案例較少,在目前公司來講,基本仍是空白,所以選擇MEF這樣一個全新的技術方案,相對其餘方案門檻較低。 函數
MEF概念的理解
可組合的部件(或簡稱「Part」):一個部件能夠向其餘部件提供服務,也可使用其餘部件提供的服務,它能夠存在任何位置,能夠是Web服務,外部系統,本系統。 學習
導出:導出是服務提供者 測試
導入:導入是服務使用者 this
約定:服務提供者與使用者之間使用的標示符,相似於身份識別。 .net
組合:對部件實例化,創建組合關係,是的導出部件和導入部件相匹配。 插件
MEF的工做原理 3d
MEF的核心包括一個catalog(目錄)和一個CompositionContainer(組合容器)。category用於發現擴展,而container用於協調建立和梳理依賴性。每一個可組合的Part提供了一個或多個Export(導出),而且一般依賴於一個或多個外部提供的服務或 Import(導入)。 orm
MEF的Demo
demo1:宿主mef ,學習compose的過程以及部件基本的特性標記
1.定義服務接口
interface IGetString { void WriteString(); }
2.定義服務的實現
[Export(typeof(IGetString))] class GetString :IGetString { public void WriteString() { Console.WriteLine("Hello Mef demo1!"); } }
3.配置宿主程序
class Program { /// <summary> /// 導入部件 /// </summary> [Import(typeof(IGetString))] public IGetString Service { get; set; } //組合部件 void Compose() { var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catelog); container.ComposeParts(this); } static void Main() { Program p = new Program(); p.Compose(); p.Service.WriteString(); Console.ReadLine(); } }
4.運行效果
demo2:多個部件的組合,學習ImportMany
當同一個接口有多個實現的時候,MEF提供了ImportMany的方式,將實現多個
[Export(typeof(IGetString))] class GetString1 :IGetString { public void WriteString() { Console.WriteLine("Hello string1!"); } } [Export(typeof(IGetString))] class GetString2 : IGetString { public void WriteString() { Console.WriteLine("Hello string2!"); } }
宿主程序代碼:
class Program { /// <summary> /// 導入 /// </summary> [ImportMany(typeof(IGetString))] public IEnumerable<IGetString> Service { get; set; } /// <summary> /// 組合 /// </summary> void Compose() { var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catelog); container.ComposeParts(this); } static void Main() { Program p = new Program(); p.Compose(); foreach (var server in p.Service) server.WriteString(); Console.ReadLine(); } }
運行效果:
demo3:多個部件和契約的配合
對export添加字符串標示信息
[Export("txt",typeof(IGetString))] class GetString :IGetString { public void WriteString() { Console.WriteLine("Hello string1!"); } } [Export("db",typeof(IGetString))] class GetString2 : IGetString { public void WriteString() { Console.WriteLine("Hello string2!"); } }
宿主程序導入部分一樣增長字符串信息,與導出部件保持一致
class Program { /// <summary> /// 導入 /// </summary> [Import("db",typeof(IGetString))] public IGetString Service { get; set; } /// <summary> /// 組合 /// </summary> void Compose() { var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catelog); container.ComposeParts(this); } static void Main() { Program p = new Program(); p.Compose(); p.Service.WriteString(); Console.ReadLine(); } }
運行效果:
demo4:Import和Export的應用場景
在MEF中,導入和導出能夠應用在類,字段,屬性,方法,並容許多個部件同時實現一個接口,和繼承的特性。
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true, Inherited = false)] public class ExportAttribute : Attribute { //...... }
導出屬性、字段或方法
class GetString { /// <summary> /// 導出屬性 /// </summary> [Export("txt")] public string GetString1 { get { return "this is a fileds!"; } } [Export(typeof(Action<string>))] public void GetString2(string name) { Console.WriteLine(name); } }
導入屬性、字段或方法
///// <summary> ///// 導入 ///// </summary> [Import("txt")] public string WriteString1 { get; set; } [Import(typeof(Action<string>))] public Action<string> WriteString2 { get; set; }
宿主程序
/// <summary> /// 組合 /// </summary> void Compose() { var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catelog); container.ComposeParts(this); } static void Main() { Program p = new Program(); p.Compose(); Console.WriteLine(p.WriteString1); p.WriteString2("this is a parameter"); Console.ReadLine(); }
輸出結果:
demo5:組合部件的嵌套
在導出部件中進行了導入操做
/// <summary> /// 服務接口 /// </summary> interface IGetString { void WriteString(); } /// <summary> ///導出部件 /// </summary> [Export("txt",typeof(IGetString))] class GetString1 :IGetString { public void WriteString() { Console.WriteLine("Hello string1!"); } } /// <summary> /// 導出部件 /// </summary> [Export("db",typeof(IGetString))] class GetString2 : IGetString { public void WriteString() { Console.WriteLine("Hello string2!"); } } /// <summary> /// 導出部件導入了其餘部件 /// </summary> [Export] class Getstring { [Import("txt",typeof(IGetString))] public IGetString Txt { get; set; } [Import("db", typeof(IGetString))] public IGetString Db { get; set; } }
宿主程序:
/// <summary> /// 導入 /// </summary> [Import] public Getstring Service { get; set; } /// <summary> /// 組合 /// </summary> void Compose() { var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catelog); container.ComposeParts(this); } static void Main() { Program p = new Program(); p.Compose(); //根據最近一層的服務提供輸出 p.Service.Txt.WriteString(); p.Service.Db.WriteString(); Console.ReadLine(); }
測試結果:
demo6:組合部件的延遲加載
部件準備工做
/// <summary> /// 服務接口 /// </summary> interface IGetString { void WriteString(); } /// <summary> ///導出部件 /// </summary> [Export("txt",typeof(IGetString))] class GetString1 :IGetString { private string initTime; public GetString1() { initTime = DateTime.Now.ToString("hh:mm:ss:"); } public void WriteString() { Console.WriteLine("部件1初始化時間:\"{0}\"",initTime); } } /// <summary> /// 導出部件 /// </summary> [Export("db",typeof(IGetString))] class GetString2 : IGetString { private string initTime; public GetString2() { initTime = DateTime.Now.ToString("hh:mm:ss:"); } public void WriteString() { Console.WriteLine("部件2初始化時間:\"{0}\"", initTime); } }
導入
/// <summary> /// 導入 /// </summary> [Import("txt",typeof(IGetString))] public IGetString Service1 { get; set; } /// <summary> /// 導出 /// </summary> [Import("db", typeof(IGetString))] public Lazy<IGetString> Service2 { get; set; }
宿主程序:
static void Main() { Program p = new Program(); //組合部件工做 p.Compose(); System.Threading.Thread.Sleep(2000); p.Service1.WriteString(); //經過延遲加載,時間間隔爲2秒 p.Service2.Value.WriteString(); Console.ReadLine(); }
輸出效果:
demo7:組合部件的繼承
導出部件,在接口中使用InheritedExport特性,在實現類中將省略Export標記
/// <summary> /// 繼承導出特性 /// </summary> [InheritedExport] interface IGetString { void WriteString(); } /// <summary> ///繼承導出功能 /// </summary> class GetString :IGetString { public void WriteString() { Console.WriteLine("這是繼承的導出部件"); } }
宿主程序:
/// <summary> /// 導入 /// </summary> [Import] public IGetString Service { get; set; } /// <summary> /// 組合 /// </summary> void Compose() { var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catelog); container.ComposeParts(this); } static void Main() { Program p = new Program(); //組合部件工做 p.Compose(); p.Service.WriteString(); Console.ReadLine(); }
組合容器(CompositionContainer)和目錄(Catalog)
通過前面的demo練習,咱們已經瞭解了MEF中的導入(Import)和導出(Export)。在本系列的第一篇文章中咱們知道MEF其實還包括另外兩個核心內容:組合容器(CompositionContainer)和目錄(Catalog)。
在宿主程序中,咱們須要經過組合容器和目錄,將部件功能引入到當前的宿主程序應用中。
/// <summary> /// 組合 /// </summary> void Compose() { var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catelog); container.ComposeParts(this); }
組合容器:比較經常使用的有CompositionContainer,而有時候會須要用到CompositionBatch,這裏講不作講解。
目錄:Assembly Catalog(程序集目錄),Directory Catalog,Aggregate Catalog,Type Catalog,和僅使用在Silverlight中得目錄Deployment Catalog( Silverlight only),Filtered Catalog.其中將着重講解Assembly Catalog(程序集目錄),Directory Catalog,Aggregate Catalog。
1.Assembly Catalog
能夠在給定的Assembly 發現全部的導出部件,使用類型AssemblyCatalog。
2.Directory Catalog
它能夠在給定的目錄(路徑,相對路徑或絕對路徑)中發現導出部件,使用類型DirectoryCatalog。若是你使用的是相對路徑,則相對的是當前AppDoamin的基路徑。DirectoryCatalog只會對給定目錄進行一次性掃描,目錄發生變化是容器不會主動刷新,若是須要刷新給定的目錄須要調用方法:Refresh() ,當目錄刷新時,容器也會從新組合部件。這個一般將一些動態連接庫(.dll)做爲部件導入到宿主程序中,能夠靈活將DirectoryCatalog中的dll增長或移除,以對同一個API實現不一樣的應用。
demo8:使用Directory Catalog
在項目中開發導出部件:經過添加類庫的方式,生成相應功能的.dll文件,其中須要遵守一個API標準(IGetString)。
1.經過MEFDemo8Service,定義API:
/// <summary> /// 定義API,同時定義爲繼承導出方式 /// </summary> [InheritedExport] public interface IGetString { void OutPut(); }
2.經過MEFDemoPart1和Part2分別實現相應的接口
分別實現了部件1和部件2的方法
/// <summary> /// 實現接口 /// </summary> public class GetString:MEFDemo8Service.IGetString { public void OutPut() { Console.WriteLine("執行了部件1的方法"); } }
3.在宿主程序中,添加API的引用以及添加分別生成part1和part2的.dll文件
其中Lib文件夾中的dll將屬性設置爲內容,並複製
在宿主程序中,經過導入,組合(使用DirectoryCatalog,指定Lib文件夾),再經過Main的執行,顯示出具體的實現內容,代碼以下:
class Program { /// <summary> /// 導入部件 /// </summary> [ImportMany(typeof(MEFDemo8Service.IGetString))] public IEnumerable<MEFDemo8Service.IGetString> Service { get; set; } //組合部件 void Compose() { var catelog = new DirectoryCatalog("Lib"); var container = new CompositionContainer(catelog); container.ComposeParts(this); } static void Main() { Program p = new Program(); p.Compose(); foreach (var server in p.Service) server.OutPut(); Console.ReadLine(); }
最終的執行效果:
當在Lib中移出了部件1
最終效果:
當部件添加到Lib中,又獲得了最開始的效果。
因而可知,使用Lib,能夠將咱們的具體實現經過物理方式隔離,在須要的時候添加,不須要的時候移除便可。
3.Aggregate Catalog
彙集目錄,有時候咱們使用單一的Assembly Catalog和Directory Catalog並不能解決咱們的需求,咱們可能須要同時使用到他們,這時候咱們即可使用Aggregate Catalog,咱們能夠將Assembly Catalog和Directory Catalog同時添加到目錄中,這種添加能夠經過構造函數實現,也能夠經過目錄集合的添加方法來實現:catalog.Catalogs.Add(...)。彙集目錄使用類型AggregateCatalog。
MEF帶來的聯想
留給你們來回答吧!!!
,