出口和中繼資料
聲明出口解釋基本的組件輸出服務和值。有些情況由於種種原因需要用出口關聯資訊。一般情況下它是用來解釋一個公用契約具體實現的效能。這可以有效地滿足允許出口或者限制出口,或者在那時匯入所有可用的實現和在使用出口之前檢查它們的運行能力。
附加中繼資料到出口
想一下我們之前的IMessageSender服務。假如我們有一些實現,而這些實現與消者有關而有所不同。在我們的例子裡訊息是否運行和是否安全對消費者(importer)是重要的資訊。
使用ExportMetadataAttribute
附加這些資訊我們只需要做的就是使用[System.ComponentModel.Composition.ExportMetadataAttribute]:
public interface IMessageSender{ void Send(string message);}[Export(typeof(IMessageSender))][ExportMetadata("transport", "smtp")]public class EmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}[Export(typeof(IMessageSender))][ExportMetadata("transport", "smtp")][ExportMetadata("secure", null)]public class SecureEmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}[Export(typeof(IMessageSender))][ExportMetadata("transport", "phone_network")]public class SMSSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}
使用自訂出口屬性
為了使它比ExportMetadataAttribute更嚴格的類型,你需要建立自己的屬性並且用[System.ComponentModel.Composition.MetadataAttribute]修飾.在這個例子裡面,我們也從ExportAttribute派生,所以建立一個自訂出口屬性也是指定的中繼資料。
[MetadataAttribute][AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]public class MessageSenderAttribute : ExportAttribute{ public MessageSenderAttribute() : base(typeof(IMessageSender)) { } public MessageTransport Transport { get; set; } public bool IsSecure { get; set; }}public enum MessageTransport{ Undefined, Smtp, PhoneNetwork, Other}
上面的例子裡,MetadataAttribute應用於自訂的出口屬性,下一步是應用屬性到IMessageSender實現。
[MessageSender(Transport=MessageTransport.Smtp)]public class EmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}[MessageSender(Transport=MessageTransport.Smtp, IsSecure=true)]public class SecureEmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}[MessageSender(Transport=MessageTransport.PhoneNetwork)]public class SMSSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}
這就是出口方所必須的。在引擎裡,MEF仍作為一個字典,但這個事實對你來說是無形的。
注意:你也可以建立中繼資料屬性不是他們自己出口,只需要通過建立使用MetadataAttributeAttribute修飾的屬性。
在這些例子裡中繼資料會被添加到出口自訂中繼資料屬性被應用的同一成員裡。
進口中繼資料
進口商可以訪問附加到出口的中繼資料。
使用強型別中繼資料
若要以強型別的方式訪問中繼資料通過定義介面匹配唯讀屬性 (名稱和類型)來建立中繼資料視圖。 對於我們的樣本來說將是類似於以下內容一個介面:
public interface IMessageSenderCapabilities{ MessageTransport Transport { get; } bool IsSecure { get; }}
於是你可以開始用System.Lazy<T, TMetadata>匯入。(T是契約類型,TMetadata是你建立的介面)
[Export]public class HttpServerHealthMonitor{ [ImportMany] public Lazy<IMessageSender, IMessageSenderCapabilities>[] Senders { get; set; } public void SendNotification() { foreach(var sender in Senders) { if (sender.Metadata.Transport == MessageTransport.Smtp && sender.Metadata.IsSecure) { var messageSender = sender.Value; messageSender.Send("Server is fine"); break; } } }}
使用弱類型中繼資料
為了以弱類型方式訪問中繼資料,你使用System.Lazy<T, TMetadata>類型進口傳遞IDictionary<string,object>中繼資料。然後你就可以通過作為字典的中繼資料屬性來訪問中繼資料。
注意:一般我們推薦用強型別方法去訪問中繼資料,然而有些系統需要通過動態方式訪問中繼資料,這也是允許的。
[Export]public class HttpServerHealthMonitor{ [ImportMany] public Lazy<IMessageSender, IDictionary<string,object>>[] Senders { get; set; } public void SendNotification() { foreach(var sender in Senders) { if (sender.Metadata.ContainsKey("Transport") && sender.Metadata["Transport"] == MessageTransport.Smtp && sender.Metadata.ContainsKey("Issecure") && Metadata["IsSecure"] == true) { var messageSender = sender.Value; messageSender.Send("Server is fine"); break; } } }}
中繼資料過濾和預設屬性值
當你選中一個中繼資料視圖,一個隱藏的過濾會觸發去匹配那些包含在視圖定義的中繼資料屬性的出口。你可以在中繼資料視圖裡通過System.ComponentModel.DefaultValueAttribute指定屬性不需要。在下面你可以看到,我們已經給IsSecure指定一個預設值false。這意味著一個組件出口IMessageSender,但沒有提供IsSecure中繼資料,但它仍然匹配。
public interface IMessageSenderCapabilities{ MessageTransport Transport { get; } [DefaultValue(false)]; bool IsSecure { get; }}