Managed Extensibility Framework (MEF) 可以讓您建立輕量型可擴充應用程式。
以下依MSDN上的計算機範例(Console程式)來說明擴充及客製的方式。
Managed Extensibility Framework (MEF) 可以讓您建立輕量型可擴充應用程式。
以下亂馬客依MSDN上的計算機範例(Console程式)來說明擴充及客製的方式。
方案如下,
1.定義Interface
1.1.計算機(ICalculator),裡面有(Calculate)計算的Method,接受一個運算字串。
1.2.運算字串(例:5-3)可區分為左運算元(5),運算子(-)及右運算元(3)
public interface ICalculator
{
string Calculate(string input);
}
public interface IOperation
{
int Operate(int left, int right);
}
//「中繼資料檢視」(Metadata View) 的介面
//http://msdn.microsoft.com/zh-tw/library/ee155691(v=vs.110).aspx
public interface IOperationData
{
char Symbol { get; }
}
2.實作 interface (專案要加入 System.ComponentModel.Composition.dll參考)
2.1.計算機
[Export(typeof(ICalculator))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MyCalculator:ICalculator
{
[ImportMany]
IEnumerable<Lazy<IOperation, IOperationData>> operations;
public string Calculate(string input)
{
int left, right;
char operation;
int foundOperatorIndex = FindFirstNonDigit(input);
if (foundOperatorIndex < 0)
{
return "Could not parse command.";
}
try
{
left = int.Parse(input.Substring(0, foundOperatorIndex));
right = int.Parse(input.Substring(foundOperatorIndex + 1));
}
catch (Exception)
{
return "Could not parse command.";
}
operation = input[foundOperatorIndex];
//透過 Metadata 找出對應的運算子
var matchOP = operations.Where((op) => op.Metadata.Symbol.Equals(operation))
.FirstOrDefault();
if (matchOP != null)
{
return matchOP.Value.Operate(left, right).ToString();
}
return "Operation Not Found!";
}
/// <summary>
/// 尋找運算子
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private int FindFirstNonDigit(string s)
{
for (int i = 0; i < s.Length; i++)
{
if(!char.IsDigit(s[i]))
{
return i;
}
}
return -1;
}
}
2.2.加、減運算子
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '+')]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Add: IOperation
{
public int Operate(int left, int right)
{
return left + right;
}
}
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '-')]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Subtract : IOperation
{
public int Operate(int left, int right)
{
return left - right;
}
}
3.程式在使用時,要讓這些 Parts 可以媒合配對(專案要加入System.ComponentModel.Composition.dll參考),
class Program
{
private CompositionContainer _container;
[Import(typeof(ICalculator))]
public ICalculator calculator;
public Program()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(MyCalculator).Assembly));
string extensionPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Extensions");
catalog.Catalogs.Add(new DirectoryCatalog(extensionPath));
_container = new CompositionContainer(catalog);
_container.ComposeParts(this);
}
static void Main(string[] args)
{
Program p = new Program();
String s;
Console.WriteLine("Enter Command:");
while (true)
{
s = Console.ReadLine();
Console.WriteLine(p.calculator.Calculate(s));
}
}
}
建置並執行程式,加、減會算出結果,但其他運算(如取餘數)就沒辦法了!
所以再來就是要擴充這個程式。
4.增加另外一個專案,來擴充計算機取餘數的能力 (專案要加入System.ComponentModel.Composition.dll參考)
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '%')]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Mod : IOperation
{
public int Operate(int left, int right)
{
return left % right;
}
}
建置完成後,將DLL放到程式的Extensions目錄之中,再執行程式,會發現計算機已經有取餘數的能力哦!
除了擴充能力外,如果想要取代原本的功能,可以在Metadata中再加入Priority屬性,
然後在找出對應的運算子的Logic中再加入取得 Priority屬性 最高的運算子,
這樣就可以達到取代原本功能的能力(客製化)。
1.Metadata View中再加入Priority屬性,因為要相容,所以設定預設值為0,這樣舊的程式就不需要多宣告這個屬性值。
public interface IOperationData
{
char Symbol { get; }
[DefaultValue(0)]
int Priority { get; }
}
2.找出對應的運算子的Logic中再加入取得 Priority屬性 最高(OrderByDescending)的運算子
var matchOP = operations.Where((op) => op.Metadata.Symbol.Equals(operation))
.OrderByDescending(op=>op.Metadata.Priority).FirstOrDefault();
3.增加客製化的加號運算子,並設定Priority屬性值為1,如下,
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '+')]
[ExportMetadata("Priority", 1)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Add: IOperation
{
public int Operate(int left, int right)
{
int bonus = 100;
//客製化 多送 100
return left + right + bonus;
}
}
建置完成後,將DLL放到程式的Extensions目錄之中,再執行程式,會發現計算機已使用客製的加號運算子!
結論
如果不喜歡設定ExportMetadata中的屬性是使用Magic String的話,可以參考 Exports and Metadata 裡面 Using a Custom Export Attribute 的部份!
除了明確的標示Import/Export外,在.NET 4.5中也提供 convention 方式來配對,詳細可參考「Managed Extensibility Framework Improvements in .NET 4.5」。
如果不想用Config去設定DI的話,可以試看看 MEF or StructureMap 哦!
Demo 方案:MEF-SimpleCalculator.zip
參考資料
Managed Extensibility Framework (MEF)
Managed Extensibility Framework Improvements in .NET 4.5
MEF開發系列 - Managed Extensibility Framework(MEF)的概念與簡介
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^