MEF初体验之六:导出和元素据
在导出声明这一节中解释了部件导出服务和值的基础知识。在某些情况下,出于多种原因,关联与导出相关的信息是有必要的。通常,它被用来解释一个指定的普通契约实现的能力。这对于允许导入约束满足它的导出,或者导入此时所有可用的实现和检查它在使用导出之前在运行时的能力是很有用的。
在Export上附加Metadata
思考这个先前介绍的IMessageSender服务。假设我们有一些实现,这些实现不同的地方可能与实现的消费者(importer)有关。针对我们的例子,消息传输及其是否安全,对于实现的消费者来说是很重要的信息。
使用ExportMetadataAttribute
我们附加这信息要做的所有事情就是使用[System.ComponentModel.Composition.ExportMetadataAttribute]:
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace ExportMetadataSample { class Program { [ImportMany] public IEnumerable<IMessageSender> Senders { get; set; } static void Main(string[] args) { Program p = new Program(); p.Compose(); foreach (var sitem in p.Senders) { IEnumerable<ExportMetadataAttribute> ems = sitem.GetType().GetCustomAttributes<ExportMetadataAttribute>(false); foreach (var eitem in ems) { Console.WriteLine(eitem.Name+","+eitem.Value); } Console.WriteLine("----------------------"); } Console.ReadKey(); } void Compose() { var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container.ComposeParts(this); } } interface IMessageSender { void Send(string msg); } [Export(typeof(IMessageSender))] [ExportMetadata("transport","smtp")] class EmailSender : IMessageSender { public void Send(string msg) { Console.WriteLine("Email sent:" + msg); } } [Export(typeof(IMessageSender))] [ExportMetadata("transport","smtp")] [ExportMetadata("secure",null)] class SecureEmailSender : IMessageSender { public void Send(string msg) { Console.WriteLine("Secure Email sent:" + msg); } } [Export(typeof(IMessageSender))] [ExportMetadata("transport","phone_network")] class SMSSender : IMessageSender { public void Send(string msg) { Console.WriteLine("SMS sent:" + msg); } } }
效果如下:
使用自定义的导出特性
为了比使用[ExportMetadata]更加强类型地到处元素据,你需要创建你自己的特性并用[Metadata]来修饰它。在这个例子中,我们也将从ExportAttribute继承,这样来创建一个自定义的也指定元素据的导出特性。
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace ExportMetadataSample { class Example { [ImportMany] IEnumerable<Lazy<IMessageSender,IMessageSenderCapabilities>> Senders { get; set; } static void Main() { Example e = new Example(); e.Compose(); foreach (var sitem in e.Senders) { Console.WriteLine(sitem.Metadata.Transport+","+sitem.Metadata.IsSecure); Console.WriteLine("----------------------"); } Console.ReadKey(); } void Compose() { var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container.ComposeParts(this); } } interface IMessageSender { } [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 IMessageSenderCapabilities { MessageTransport Transport { get; } bool IsSecure { get; } } [MessageSender(Transport = MessageTransport.Smtp)] public class EmailSender : IMessageSender { public void Send(string msg) { Console.WriteLine("Email sent:" + msg); } } [MessageSender(Transport = MessageTransport.Smtp, IsSecure = true)] public class SecureEmailSender : IMessageSender { public void Send(string msg) { Console.WriteLine("Secure Email sent:" + msg); } } [MessageSender(Transport = MessageTransport.PhoneNetWork)] public class SMSSender : IMessageSender { public void Send(string msg) { Console.WriteLine("SMS sent:" + msg); } } }
为了以一种强类型的方式访问元素据,可以通过定义一个匹配只读属性的接口来创建一个元数据视图。然后,你可以开始用System.Lazy<T, TMetadata>来导入,其中T是契约类型,而TMetadata是你创建的接口。输出如图:
使用弱类型元数据
为了以一种弱类型费那个事访问元素据,你通过使用System.Lazy<T, TMetadata>,并向其TMetadata泛型参数传递IDictionary<string,object>来作为元数据来导入,然后,你可以将Lazy<T,TMetadata>的Metadata转换为IDictionary<string,object>类型来访问。注意:一般情况,我们建议使用强类型来访问元数据,然而,有一些系统需要动态方式访问元数据,弱类型方式允许你这么做。