天天看點

實戰MEF(5):導出中繼資料

如何了解元數

我們可以把中繼資料了解為随類型一起導出的附加資訊。有時候我們會考慮,把中繼資料随類型一并導出,增加一些說明,使得我們在導入的時候,可以多一些篩選條件。

預設的類型導出帶有中繼資料嗎

上面的内容我說得比較簡潔,也許您不是很了解,不要緊,在程式設計裡面,很多東西我們都是寫了代碼後才了解的。是以,我的理論功底比較差,最不擅長的就是長篇大論,還是從代碼中看吧。

我們首先要弄清楚一下問題:在我沒有手動去添加中繼資料的預設導出類型,是否帶有中繼資料。為了使代碼更簡單,這裡我直接把一個類導出,而不編寫公共接口了。

實戰MEF(5):導出中繼資料

這裡我們直接編寫一個類,然後直接導入這個類型即可:

實戰MEF(5):導出中繼資料

示例代碼定在目前程式集中,可以在AssemblyCatalog範圍查找。

實戰MEF(5):導出中繼資料

AssemblyCatalog的Parts屬性在智能提示中沒有出現(從ComposablePartCatalog類繼承下來,可能是因為虛方法沒有被重寫,是以沒有在智能提示中顯示出來),不過的确有這個屬性,通過枚舉Parts通路每個導出的元件類,而在ExportDefinitions屬性中的每個ExportDefinition對象都有一個Metadata屬性,它就是每個導出的中繼資料,為字典類型(IDictionary<string, object>),key是字元串類型,value是任意對象(Object)。

運作應用程式後,我們會看到如下圖所示的内容:

實戰MEF(5):導出中繼資料

這個例子表明,在預設情況下,導出是帶有中繼資料的,從上面的運作結果可以猜到預設的中繼資料是用于說明導出元件的類型的。

如何導出中繼資料?

要導出中繼資料,除了對目标類型應用ExportAttribute特性外,還要用ExportMetadataAttribute特性來定義元數,在定義時遵循字典結構,即構造函數的兩個參數分别代表key和value。如下面代碼:

實戰MEF(5):導出中繼資料

這兩個中繼資料标記本元件的版本為200,作者是小王。我們知道中繼資料是IDictionary<string, object>字典結構,這就好辦,我們在導入的時候使用Lazy<T, TMetadata>,以前我們用過Lazy<T>,現在因為帶了中繼資料,是以就用Lazy<T, TMetadata>,然後讓TMetadata的類型為IDictionary<string, object>就可以了。示例代碼如下:

實戰MEF(5):導出中繼資料

AssemblyCatalog cat = new AssemblyCatalog(typeof(Program).Assembly);

// 組裝

CompositionContainer container = new CompositionContainer(cat);

Program p = new Program();

try

{

container.ComposeParts(p);

// 顯示中繼資料

if (p.f_task.Metadata.ContainsKey("Ver"))

Console.WriteLine("版本号:{0}。", p.f_task.Metadata["Ver"].ToString());

}

if (p.f_task.Metadata.ContainsKey("Author"))

Console.WriteLine("作者:{0}。", p.f_task.Metadata["Author"].ToString());

Console.Write("\n\n");

// 測試調用

p.f_task.Value.OutPut();

catch(Exception ex)

Console.WriteLine(ex.Message);

finally

container.Dispose();

好了,運作一下,如圖所示,我們已經把中繼資料也導入了。

實戰MEF(5):導出中繼資料

還有另一種較為複雜的中繼資料導出導入方式,那就是自己實作的強類型中繼資料。我們來動手做做。

  1. 定義一個表示中繼資料的公共接口,名為ICustMetadata。

[MetadataViewImplementation(typeof(MyCustMetaData))]

public interface ICustMetadata

int Ver { get; } //版本

string Author { get; } //作者

在定義接口時,并加上MetadataViewImplementation特性,且指明哪些類将實作該接口。

2、上面我們指定了實作ICustMetadata的類為MyCustMetaData,是以接下來我們要定義這個類。

public class MyCustMetaData:ICustMetadata

IDictionary<string, object> m_dic;

// 構造函數

public MyCustMetaData(IDictionary<string, object> _pDic)

this.m_dic = _pDic;

public int Ver

get

if (m_dic.ContainsKey("Ver"))

return Convert.ToInt32(m_dic["Ver"]);

return -1;

public string Author

if (m_dic.ContainsKey("Author"))

return m_dic["Author"].ToString();

return string.Empty;

注意:定義中繼資料視圖類時,必須包含帶有一個IDictionary<string, object>類型參數的構造函數,否則将無法使用。我們通過前面的内容知道中繼資料其實是以字典形式存在的,故傳給中繼資料視圖類的構造函數的就是中繼資料的原始視圖,隻是我們用一個類來重封裝了一下而已。

4、把調用的代碼修改如下:

class Program

[Import]

public Lazy<TestTask, ICustMetadata> f_task;

static void Main(string[] args)

AssemblyCatalog cat = new AssemblyCatalog(typeof(Program).Assembly);

Console.WriteLine("中繼資料視圖類型:{0}。", p.f_task.Metadata.GetType().Name);

Console.WriteLine("------- 中繼資料如下 --------");

Console.WriteLine("版本:{0}", p.f_task.Metadata.Ver);

Console.WriteLine("作者:{0}", p.f_task.Metadata.Author);

Console.Read();

最後就得到如下圖所示的結果:

實戰MEF(5):導出中繼資料

中繼資料的實際類型正好是我們上面定義的MyCustMetaData類。

今天就到此為止吧,88各位。

MEF