MEF 编程指南(六):导出和元数据
声明导出解释了部件导出服务的基础知识和价值观(Values)。有时候出于种种原因,导出关联信息是非常必要的。通常,用于解释关于功能公共契约的具体实现。允许导入满足约束要求的导出,或者导入所有可用的实现并且在导出前在运行时检查他们的功能。
为导出附加元数据(Attaching Metadata to an Export)
考虑到 IMessageSender 服务更早引入。假设我们有一些实现,和相关的消费者实现(Consumer Of The Implementations)有差异。对于我们的示例,消息的传送(Transport)和是否安全(Secure)对于消费者(导入部件)都是重要的信息。
使用 ExportMetadataAttribute 特性
所有我们需要做的事情是使用 [System.ComponentModel.Composition.ExportMetadataAtrribute] 附加这些信息。
[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); } } public interface IMessageSender { void Send(string message); }
使用自定义导出特性(Using a Custom Export Attribute)
为了使用比 ExportMetadata 更强类型,需要创建自定义特性并且用 [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 } public interface IMessageSender { void Send(string message); }
以上,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 机制下依然会填充一个字典(Populating A Dictionary),但是这对用户不可见。
注意:你也可以创建没有自身导出的元数据特性,通过创建一个使用 MetadataAttribute 标识的特性。在这些情况下,元数据会被添加到有相同成员的应用自定义元数据特性的导出。
导入元数据(Importing Metadata)
导入部件能访问附加到导出的元数据。
使用强类型元数据(Using Strongly-typed Metadata)
为了访问强类型的元数据,通过定义匹配只读属性(名称和类型)的接口创建元数据视图。对于我们示例,像下面的接口:
public interface IMessageSenderCapabilities { MessageTransport Transport { get; } bool IsSecure { get; } }
然后,就可以使用 System.Lazy<T, TMetadata> 类型开始导入,其中 T 是契约类型,TMetadata 是创建的接口。
public class HttpServerHealthMonitor { [ImportMany] public Lazy<IMessageSender, IMessageSenderCapabilities>[] Senders { get; set; } public void SendNotification() { Compose(); foreach (var sender in Senders) { if (sender.Metadata.Transport == MessageTransport.Smtp && sender.Metadata.IsSecure) { var messageSender = sender.Value; messageSender.Send("Server is fine"); break; } } } private void Compose() { //var container = new CompositionContainer(); //container.ComposeParts(this, new EmailSender()); AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container.ComposeParts(this); } }
使用弱类型元数据(Using Weakly-Typed Metadata)
为了用弱类型的方式访问元数据,使用 System.Lazy<T, TMetadata> 类型传递 IDictionary<string, object> 元数据给导入。然后,可以通过 Dictionary 的元数据属性访问元数据。
注意:通常,我们建议通过强类型方法访问元数据,然后有些系统需要允许以动态形式访问元数据。
public class HttpServerHealthMonitor { [ImportMany] public Lazy<IMessageSender, IDictionary<string, object>>[] Senders { get; set; } public void SendNotification() { Compose(); foreach (var sender in Senders) { if (sender.Metadata.ContainsKey("Transport")) { var messageSender = sender.Value; messageSender.Send("Server is fine"); } } } private void Compose() { //var container = new CompositionContainer(); //container.ComposeParts(this, new EmailSender()); AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container.ComposeParts(this); } }
元数据过滤和 DefaultValueAttribute 特性(Metadata Filtering and DefaultValueAttribute)
当指定元数据视图(Metadata View)的时候,隐式过滤将会匹配那些在视图定义中满足包含元数据属性条件的导出。你可以使用 System.ComponentModel.DefaultValueAttribute 特性指定一个元数据视图是非必要的,下面你可以看到,我们在 IsSecure 上指定默认值(Default Value)为假(False)。这意味着,如果一个部件导出 IMessageSender,但是没有提供 IsSecure 元数据,也仍将被匹配。
public interface IMessageSenderCapabilities { MessageTransport Transport { get; } [DefaultValue(false)] bool IsSecure { get; } }
原文地址: