[C#]MEF開發系列 - Managed Extensibility Framework(MEF)的概念與簡介
Managed Extensibility Framework(MEF)是.NET 4.0與Silverlight 4.5開始內建在BCL中的輕量型可擴充式框架(若是使用.NET 3.5,也可以另行加入組件使用),可以輔助開發人員建立具有擴充彈性且易於維護的應用程式。
在以往若是我們要為程式加上可擴充性的功能,可能必須要使用反射來做,可參閱筆者使用反射(Reflection)實現應用程式擴充元件機制這篇。為了要達到類似的效果,我們必需去找出目錄所內含的組件檔,循序的去將找到的組件載入,載入後遍循所有可被實體化的類別,並從中找出有實作指定介面的類別,再將其實體化後塞入正確的位置。在做這樣的動作時,目錄內可能會存放著非.net的組件,不小心載入系統就會丟出例外而讓整個載入流程中斷。而將類別實體化為物件時,又需考慮類別的建構子,建構子不對也無法正確地將類別實體化為物件。而且做這樣的動作多半必需依賴著特定型別或介面去做。當系統中的擴充點一多,這樣的動作就得成倍數成長,十分的不好處理。
MEF的出現可以讓開發人員有效的簡化這樣的動作,開發時開發人員只要知道哪邊是會有變動或是需要擴充的點、要塞入擴充點的動作為何、以及甚麼時候要將擴充的動作塞入擴充點。擴充功能的探索以及擴充功能與擴充點的組合,MEF都做了相當的封裝,使用起來非常的方便,而前面提到載入時可能發生的問題MEF也都處理掉了,開發人員可以更專注於擴充點與擴充功能的建立。
為了易於了解,這邊我們可以把擴充點想成是插座(母頭),擴充的功能是電器的插頭(公頭)。每個插座雖然在外觀上會有些差異(接口不同),但都是提供電器運轉所需要的電,而每個電器也都有著不同的功能,只要能插到正確的插座就能夠發揮電器該有的效果。同一個插座可能依插上的電器不同一會具有照明的功能,一會具有發熱的功能,或是透過擴充插座同時照明與發熱,這就是我們程式開發所需要的擴充彈性。而MEF在這邊扮演的就是一個代理人,只要我們將插座與插頭交給它,插頭就會插在正確的插座上,它自行會依照一些線索(插座與插頭的外觀)下去推斷要做怎樣的組合,不用煩惱它到底是歐規、美規、兩頭、三頭、110v、220v...
MEF主要是由下列幾個部分所組成
- Composable part
- Export
- Import
- Composition Container
- Catalog
Composable part 是composable unit,也就是最小的組成單位,可以提供服務給其他組件使用(擴充功能)或是操作其他組件提供的服務(擴充點)。
Export 是指提供的服務,也就是我們所說的擴充功能。透過ExportAttribute來設定。
Import 是指所使用的服務,也就是我們所說的擴充點。透過ImportAttribute來設定。
Composition Container 是組合容器,會去mapping import跟export、組合Composable part。
Catalog 主要是用來探索Composable part用的,可從型別、組件 (Assembly) 或目錄 (Directory)探索到Composable part。
了解了MEF的概念以及組成元件後,讓我們來看一下MEF官方的示意圖片。由下圖中我們可以很清楚的看到Composable part內具有Import與Export,而Export與Import可以透過MEF做組合,表示著透過MEF我們可以很容易的在程式撰寫時透過Attribute去指定擴充點與擴充功能,並透過MEF將它組合起來達到我們想要的效果。簡單的一張圖帶出了上面提到的Import、Export、Composable Part、以及Composition等概念。
而下圖則是表示著Catalog可以幫我們探索Composable part,並且透過Composition Container元件我們可以將之組合在一起。
這邊讓我們實際來寫個簡易的MEF程式。為了便於比較,這邊筆者直接拿使用反射(Reflection)實現應用程式擴充元件機制這篇的範例稍作修改,從反射叫用改用MEF下去實作。
首先我們必須將System.ComponentModel.Composition.dll加入參考。
然後在程式中引用System.ComponentModel.Composition命名空間:
using System.ComponentModel.Composition;
接著將PlugIn.Core的內的Interface做些調整。
namespace PlugIn.Core
{
public interface IHost
{
IEnumerable<IModule> Modules { get; set; }
}
}
並在PlugIn.Core.IModule上附加InheritedExportAttribute(前面所提到的Export),用以指定所有實作該介面的類別都視為我們程式中的擴充功能。
namespace PlugIn.Core
{
[InheritedExport]
public interface IModule
{
String Name { get; }
IHost Host { get; set; }
void Execute();
}
}
最後要調整的是PlugIn.Host.MainForm,將之實作IHost介面,在Modules成員屬性上附加ImportManyAttribute(前面提到的Import),用以指定Modules成員屬性為我們程式中的擴充點,並在建構子中透過DirectoryCatalog(前面提到的Catalog)找出當前目錄下有哪些可擴充的元件(Composable part),然後用CompositionContainer將擴充點與擴充功能組合就可以了。
namespace Host
{
public partial class MainForm : Form, IHost
{
...
[ImportMany]
public IEnumerable<IModule> Modules { get; set; }
public MainForm()
{
InitializeComponent();
var catalog = new DirectoryCatalog(Environment.CurrentDirectory, "*.dll");
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
foreach (IModule module in Modules)
{
module.Host = this;
模組MToolStripMenuItem.DropDownItems.Add(module.Name, null, Module_Click).Tag = module;
}
}
...
}
}
程式撰寫起來比使用反射來說簡單了許多,使用反射(Reflection)實現應用程式擴充元件機制這篇範例內用到的PlugInController整個都可以拿掉了。
運行結果如下:
Download
Link
- Managed Extensibility Framework
- Managed Extensibility Framework - Wikipedia, the free encyclopedia
- Building Composable Apps in .NET 4 with the Managed Extensibility Framework
- [VS2010 Online]Managed Extensibility Framework in Visual Studio 2010 -- (1)
- MEF (Managed Extensibility Framework) in .NET 4.0
- Extending Microsoft.Composition with Alternative Programming Models
- DotNet 4 學習筆記之1-------------MEF (Managed Extensibility Framework)