在.NET中只使用MEF获取必要的插件

我有IMessageSender界面。

using System.ComponentModel.Composition; public interface IMessageSender { void Send(string message); } 

我有两个实现此接口的插件。 这是plugin.cs。

 using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System; [Export(typeof(IMessageSender))] public class EmailSender : IMessageSender { public void Send(string message) { Console.WriteLine(message); } } 

这是plugin2.cs

 [Export(typeof(IMessageSender))] public class EmailSender : IMessageSender { public void Send(string message) { Console.WriteLine(message + "!!!!"); } } 

我有这个代码用MEF运行这些插件。

 using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System.Collections.Generic; using System; public class Program { [ImportMany] public IEnumerable MessageSender { get; set; } public static void Main(string[] args) { Program p = new Program(); p.Run(); foreach (var message in p.MessageSender) { message.Send("hello, world"); } } public void Run() { Compose(); } private void Compose() { var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new DirectoryCatalog(@"./")); var container = new CompositionContainer(catalog); container.ComposeParts(this); } } 

编译后,我得到了我想要的东西。

 > mono program.exe hello, world hello, world!!!! 

我的问题是如何选择性地耗尽许多插件。 这个例子只是获取所有可用的插件来运行所有这些插件,但是当我只想运行第一个插件或第二个插件时,我该怎么办?

例如,我可以如下运行plugin2.dll吗?

 public static void Main(string[] args) { Program p = new Program(); p.Run(); var message = messageSender.GetPlugin("plugin"); // ??? message.Send("hello, world"); } 

解决了

基于这个网站 ,以及Matthew Abbott的回答。 我可以拿出这个工作代码。

接口代码(interface.cs)

 using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System; public interface IMessageSender { void Send(string message); } public interface IMessageSenderMetadata { string Name {get; } string Version {get; } } [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class MessageMetadataAttribute : ExportAttribute, IMessageSenderMetadata { public MessageMetadataAttribute( string name, string version) : base(typeof(IMessageSender)) { Name = name; Version = version; } public string Name { get; set; } public string Version { get; set; } } 

插件代码(Plugin.cs …)

 using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System; [MessageMetadataAttribute("EmailSender1", "1.0.0.0")] public class EmailSender : IMessageSender { public void Send(string message) { Console.WriteLine(message + "????"); } } 

Program.cs中

 using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System.Collections.Generic; using System; using System.Linq; public class Program { [ImportMany(typeof(IMessageSender), AllowRecomposition = true)] public IEnumerable<Lazy> Senders { get; set; } public static void Main(string[] args) { Program p = new Program(); p.Run(); var sender1 = p.GetMessageSender("EmailSender1","1.0.0.0"); sender1.Send("hello, world"); sender1 = p.GetMessageSender("EmailSender2","1.0.0.0"); sender1.Send("hello, world"); } public void Run() { Compose(); } public IMessageSender GetMessageSender(string name, string version) { return Senders .Where(l => l.Metadata.Name.Equals(name) && l.Metadata.Version.Equals(version)) .Select(l => l.Value) .FirstOrDefault(); } private void Compose() { var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new DirectoryCatalog(@"./")); var container = new CompositionContainer(catalog); container.ComposeParts(this); } } 

MEF支持导出自定义元数据以配合导出的类型。 您需要做的是首先定义MEF将用于创建包含元数据的代理对象的接口。 在您的示例中,您可能需要为每个导出创建唯一名称,因此我们可以定义:

 public interface INameMetadata { string Name { get; } } 

您需要做的是确保为需要它的每个导出分配元数据:

 [Export(typeof(IMessageSender)), ExportMetadata("Name", "EmailSender1")] public class EmailSender : IMessageSender { public void Send(string message) { Console.WriteLine(message); } } 

MEF将做什么,是使用存储在ExportMetadata("Name", "EmailSender1") atrribute中的值生成一个项目实现您的接口,INameMetadata。

完成后,您可以进行一些过滤,因此将[Import]重新定义为:

 [ImportMany] public IEnumerable> Senders { get; set; } 

MEF将创建的是可枚举的Lazy实例,它们支持实例类型的延迟实例化。 我们可以查询为:

 public IMessageSender GetMessageSender(string name) { return Senders .Where(l => l.Metadata.Name.Equals(name)) .Select(l => l.Value) .FirstOrDefault(); } 

使用参数"EmailSender1"name参数运行此操作将导致返回我们的EmailSender实例。 需要注意的重要一点是,我们如何基于查询与该类型相关联的元数据来选择要使用的特定实例。

您可以更进一步,并且可以将ExportExportMetadata属性合并为单个属性,例如:

 [AttributeUsage(AttributeTargets.Class, AllowMultiple=false), MetadataAttribute] public class ExportMessageSenderAttribute : ExportAttribute, INameMetadata { public ExportMessageSenderAttribute(string name) : base(typeof(IMessageSender)) { Name = name; } public string Name { get; private set; } } 

这允许我们使用单个属性来导出类型,同时仍然提供额外的元数据:

 [ExportMessageSender("EmailSender2")] public class EmailSender : IMessageSender { public void Send(string message) { Console.WriteLine(message); } } 

显然,以这种方式查询会为您提供设计决策。 使用Lazy实例意味着您将能够推迟实例的实例化,但这确实意味着每个延迟只能创建一个实例。 MEF框架的Silverlight变体还支持ExportFactory类型,它允许您每次启动T新实例,仍然为您提供丰富的元数据机制。