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

pro*_*eek 8 .net c# plugins mef

我有IMessageSender界面.

using System.ComponentModel.Composition;

public interface IMessageSender
{
    void Send(string message);
}
Run Code Online (Sandbox Code Playgroud)

我有两个实现此接口的插件.这是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是plugin2.cs

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

我有这个代码用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

> mono program.exe 
hello, world
hello, world!!!!
Run Code Online (Sandbox Code Playgroud)

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

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

public static void Main(string[] args)
{
    Program p = new Program();
    p.Run();

    var message = messageSender.GetPlugin("plugin"); // ???
    message.Send("hello, world");
}
Run Code Online (Sandbox Code Playgroud)

解决了

基于这个网站,以及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; }  
}
Run Code Online (Sandbox Code Playgroud)

插件代码(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 + "????");
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*ott 19

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

public interface INameMetadata
{
  string Name { get; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

完成后,您可以进行一些过滤,然后将您的内容重新定义[Import]为:

[ImportMany]
public IEnumerable<Lazy<IMessageSender, INameMetadata>> Senders { get; set; }
Run Code Online (Sandbox Code Playgroud)

MEF将创建的是可枚举的Lazy<T, TMetadata>实例,这些实例支持实例类型的延迟实例化.我们可以查询为:

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

使用参数"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; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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