获得只需要的插件与MEF在.NET [英] Getting only necessary plugins with MEF in .NET

查看:198
本文介绍了获得只需要的插件与MEF在.NET的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有IMessageSender接口。

 使用System.ComponentModel.Composition;

公共接口IMessageSender
{
    发送无效(字符串消息);
}
 

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

 使用System.ComponentModel.Composition;
使用System.ComponentModel.Composition.Hosting;
使用的System.Reflection;
使用系统;

[导出(typeof运算(IMessageSender))]
公共类EmailSender:IMessageSender
{
    公共无效发送(字符串消息)
    {
        Console.WriteLine(消息);
    }
}
 

这是plugin2.cs

  [导出(typeof运算(IMessageSender))]
公共类EmailSender:IMessageSender
{
    公共无效发送(字符串消息)
    {
        Console.WriteLine(信息+!!!!);
    }
}
 

和我有这个code与MEF运行这些插件。

 使用System.ComponentModel.Composition;
使用System.ComponentModel.Composition.Hosting;
使用的System.Reflection;
使用System.Collections.Generic;
使用系统;

公共类节目
{
    [ImportMany]
    公开的IEnumerable< IMessageSender> MessageSender {获得;组; }

    公共静态无效的主要(字串[] args)
    {
        程序P =新计划();
        p.Run();

        的foreach(在p.MessageSender VAR消息){
            message.Send(你好,世界);
        }
    }

    公共无效的run()
    {
      撰写();
    }

    私人无效撰写()
    {
        变种目录=新AggregateCatalog();
        catalog.Catalogs.Add(新DirectoryCatalog(@./));

        VAR集装箱=​​新CompositionContainer中(目录);
        container.ComposeParts(本);
    }
}
 

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

 >单PROGRAM.EXE
你好,世界
你好,世界!!!!
 

我的问题是我怎么能选择性地跑了许多的插件。这个例子只是获取所有可用的插件来运行所有的人,但我该怎么办时,我只是想先运行插件或第二插件?

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

 公共静态无效的主要(字串[] args)
{
    程序P =新计划();
    p.Run();

    VAR消息= messageSender.GetPlugin(插件); // ???
    message.Send(你好,世界);
}
 

求解

根据<α

href="http://devlicio.us/blogs/derik_whittaker/archive/2010/03/06/providing-metadata-to-you-mef-exports.aspx"相对=nofollow>这个网站,和马修雅培的回答。我能想出这个工作code。

接口code(interface.cs)

 使用System.ComponentModel.Composition;
使用System.ComponentModel.Composition.Hosting;
使用的System.Reflection;
使用系统;

公共接口IMessageSender
{
    发送无效(字符串消息);
}

公共接口IMessageSenderMetadata
{
    字符串名称{; }
    字符串版本{获得; }
}

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class,的AllowMultiple = FALSE)]
公共类MessageMetadataAttribute:ExportAttribute,IMessageSenderMetadata
{
    公共MessageMetadataAttribute(字符串名称,字符串版本)
            :基地(typeof运算(IMessageSender))
        {
            名称=名称;
            版本=版本;
        }

    公共字符串名称{;组; }
    公共字符串版本{获得;组; }
}
 

插件code(Plugin.cs ...)

 使用System.ComponentModel.Composition;
使用System.ComponentModel.Composition.Hosting;
使用的System.Reflection;
使用系统;

[MessageMetadataAttribute(EmailSender1,1.0.0.0)]
公共类EmailSender:IMessageSender
{
    公共无效发送(字符串消息)
    {
        Console.WriteLine(信息+????);
    }
}
 

的Program.cs

 使用System.ComponentModel.Composition;
使用System.ComponentModel.Composition.Hosting;
使用的System.Reflection;
使用System.Collections.Generic;
使用系统;
使用System.Linq的;

公共类节目
{
    [ImportMany(typeof运算(IMessageSender),AllowRecomposition =真)
    公开的IEnumerable&LT;懒&LT; IMessageSender,IMessageSenderMetadata&GT;&GT;发件人{获得;组; }

    公共静态无效的主要(字串[] args)
    {
        程序P =新计划();
        p.Run();

        变种sender1 = p.GetMessageSender(EmailSender1,1.0.0.0);
        sender1.Send(你好,世界);
        sender1 = p.GetMessageSender(EmailSender2,1.0.0.0);
        sender1.Send(你好,世界);
    }

    公共无效的run()
    {
      撰写();
    }

    公共IMessageSender GetMessageSender(字符串名称,字符串版本)
    {
      返回发件人
        。凡(升=&GT; l.Metadata.Name.Equals(名称)及&安培; l.Metadata.Version.Equals(版本))
        。选择(升=&GT; l.Value)
        .FirstOrDefault();
    }

    私人无效撰写()
    {
        变种目录=新AggregateCatalog();
        catalog.Catalogs.Add(新DirectoryCatalog(@./));

        VAR集装箱=​​新CompositionContainer中(目录);
        container.ComposeParts(本);
    }
}
 

解决方案

MEF支持自定义元数据的导出陪你导出的类型。你需要做的,是首先定义MEF将用它来创建一个包含元数据的代理对象的接口。在你的榜样,你可能会为每个出口需要有一个独特的名字,所以我们可以定义:

 公共接口INameMetadata
{
  字符串名称{; }
}
 

你会怎么那么需要做的,就是确保你分配的元数据,为每个出口需要它的:

  [导出(typeof运算(IMessageSender)),ExportMetadata(姓名,EmailSender1)
公共类EmailSender:IMessageSender
{
  公共无效发送(字符串消息)
  {
    Console.WriteLine(消息);
  }
}
 

什么MEF会做的,是生成一个项目中的接口, INameMetadata 使用存储在 ExportMetadata(名称的价值的实现, EmailSender1) atrribute。

在你这样做,你可以做一些过滤,从而重新定义你的 [导入] 来是这样的:

  [ImportMany]
公开的IEnumerable&LT;懒&LT; IMessageSender,INameMetadata&GT;&GT;发件人{获得;组; }
 

什么MEF将创建为的枚举懒&LT; T,TMetadata&GT; 支持推迟您的实例类型的实例的实例。我们可以根据查询:

 公共IMessageSender GetMessageSender(字符串名称)
{
  返回发件人
    。凡(L =&GT; l.Metadata.Name.Equals(名称))
    。选择(升=&GT; l.Value)
    .FirstOrDefault();
}
 

EmailSender1参数运行此名称参数将导致我们的<$实例C $ C> EmailSender 返回。要注意的重要的事情是我们如何选择具体的实例来使用,根据查询的类型相关的元数据。

您可以去一个另外的,你可以合并的导出 ExportMetadata 属性到一个单一的属性,例如这样的:

  [AttributeUsage(AttributeTargets.Class,的AllowMultiple = FALSE),MetadataAttribute]
公共类ExportMessageSenderAttribute:ExportAttribute,INameMetadata
{
  公共ExportMessageSenderAttribute(字符串名称)
    :基地(typeof运算(IMessageSender))
  {
    名称=名称;
  }

  公共字符串名称{;私定; }
}
 

这允许我们使用一个单独的属性导出类型,同时还提供了额外的元数据:

  [ExportMessageSender(EmailSender2)
公共类EmailSender:IMessageSender
{
  公共无效发送(字符串消息)
  {
    Console.WriteLine(消息);
  }
}
 

显然,这种查询方式presents你一个设计决策。使用延迟&LT; T,TMetadata&GT; 实例意味着你就可以推迟实例的实例化,但是这并不意味着只有一个实例可以为每个偷懒被创建。在MEF框架的Silverlight的变体还支持 ExportFactory&LT; T,TMetadata&GT; 类型,它可以让你旋转起来 T的新实例每一次,whilist仍然为您提供丰富的元数据的机制。

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);
    }
}

解决方案

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:

public interface INameMetadata
{
  string Name { get; }
}

What you would then need to do, is make sure you assign that metadata for each of your exports that require it:

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

What MEF will do, is generate a project an implementation of your interface, INameMetadata using the value stored in the ExportMetadata("Name", "EmailSender1") atrribute.

After you've done that, you can do a little filtering, so redefine your [Import] to something like:

[ImportMany]
public IEnumerable<Lazy<IMessageSender, INameMetadata>> Senders { get; set; }

What MEF will create is an enumerable of Lazy<T, TMetadata> instances which support deferred instantiation of your instance type. We can query as:

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

Running this with an argument of "EmailSender1" for the name parameter will result in our instance of EmailSender 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 and ExportMetadata attributes into a single attribute, such like:

[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; }
}

This allows us to use a single attribute to export a type, whilst still providing additional metadata:

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

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 the ExportFactory<T, TMetadata> type, which allows you to spin up new instances of T each time, whilist still providing you with the rich metadata mechanism.

这篇关于获得只需要的插件与MEF在.NET的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆