在 .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<IMessageSender> 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");
}
已解决
基于 此网站,以及马修·阿博特的回答。我可以想出这个工作代码。
接口代码 (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<IMessageSender, IMessageSenderMetadata>> 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);
}
}
I have IMessageSender interface.
using System.ComponentModel.Composition;
public interface IMessageSender
{
void Send(string message);
}
And I have two plugins that implements this interface. This is 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);
}
}
and this is plugin2.cs
[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine(message + "!!!!");
}
}
And I have this code to run those plugins with 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<IMessageSender> 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);
}
}
After compilation, I get what I want.
> mono program.exe
hello, world
hello, world!!!!
My question is how can I selectively run out of many plugins. This example just gets all the available plugins to run all of them, but what should I do when I just want to run first plugin or second plugin?
For example, can I run only plugin2.dll as follows?
public static void Main(string[] args)
{
Program p = new Program();
p.Run();
var message = messageSender.GetPlugin("plugin"); // ???
message.Send("hello, world");
}
SOLVED
Based on this site, and Matthew Abbott's answer. I could come up with this working code.
interface code (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 code (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<IMessageSender, IMessageSenderMetadata>> 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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
MEF 支持导出自定义元数据以配合导出的类型。您需要做的是首先定义一个接口,MEF 将使用该接口来创建包含元数据的代理对象。在您的示例中,您可能需要为每个导出提供唯一的名称,因此我们可以定义:
然后您需要做的是确保为需要它的每个导出分配该元数据:
MEF 将做什么,使用存储在
ExportMetadata("Name", "EmailSender1")
属性中的值生成一个接口实现的项目,INameMetadata
。完成此操作后,您可以进行一些过滤,因此将
[Import]
重新定义为:MEF 将创建的是的枚举。 code> 支持实例类型的延迟实例化的实例。我们可以这样查询:
Lazy
Lazy使用
name
参数的"EmailSender1"
参数运行此命令将导致返回EmailSender
实例。需要注意的重要一点是我们如何根据查询与该类型关联的元数据来选择要使用的特定实例。您可以更进一步,您可以将
Export
和ExportMetadata
属性合并为单个属性,例如:这允许我们使用单个属性来导出类型,同时仍然提供额外的元数据:
显然,以这种方式查询可以为您提供设计决策。使用
Lazy
实例意味着您将能够推迟实例的实例化,但这确实意味着每个惰性实例只能创建一个实例。 MEF 框架的 Silverlight 变体还支持ExportFactory
类型,它允许您每次启动T
的新实例,同时仍为您提供丰富的元数据机制。MEF supports the exporting of custom metadata to accompany your exported types. What you need to do, is first define an interface that MEF will use to create a proxy object containing your metadata. In your example, you'll likely need a unique name for each export, so we could define:
What you would then need to do, is make sure you assign that metadata for each of your exports that require it:
What MEF will do, is generate a project an implementation of your interface,
INameMetadata
using the value stored in theExportMetadata("Name", "EmailSender1")
atrribute.After you've done that, you can do a little filtering, so redefine your
[Import]
to something like:What MEF will create is an enumerable of
Lazy<T, TMetadata>
instances which support deferred instantiation of your instance type. We can query as:Running this with an argument of
"EmailSender1"
for thename
parameter will result in our instance ofEmailSender
being returned. The important thing to note is how we've selected a specific instance to use, based on querying the metadata associated with the type.You can go one further, and you could amalgamate the
Export
andExportMetadata
attributes into a single attribute, such like:This allows us to use a single attribute to export a type, whilst still providing additional metadata:
Obviously querying this way presents you with a design decision. Using
Lazy<T, TMetadata>
instances means that you'll be able to defer instantiation of the instance, but that does mean that only one instance can be created per lazy. The Silverlight variant of the MEF framework also supports theExportFactory<T, TMetadata>
type, which allows you to spin up new instances ofT
each time, whilist still providing you with the rich metadata mechanism.