MEF导出部件的元数据

4
我想使用MEF作为我正在构建的应用程序的插件系统。我希望每个组件都有一个标识符(GUID),我希望能够根据此标识符进行查找。但是,当使用导出部件时,此ID也是有用的。
是否有一种方法可以通过元数据属性来包含ID以及导出部分上的属性(或方法),而不需要开发人员填写两次或使用反射从属性中查找它?
2个回答

7

这可能是一个MEF元数据属性和一个抽象基类的组合。我会将我的插件契约定义为:

public interface IPluginMetadata
{
  Guid PluginId { get; }
}

public interface IPlugin : IPluginMetadata
{
  void Initialise();
}

我强制要求 IPlugin 接口也继承我们的元数据合同 IPluginMetadata。接下来,我们可以创建一个自定义的导出属性:

[AttributeUsage(AttributeTargets.Class, Inherit = true), MetadataAttribute]
public class ExportPluginAttribute : ExportAttribute, IPluginMetadata
{
  public ExportPluginAttribute(string pluginId) : base(typeof(IPlugin))
  {
    if (string.IsNullOrEmpty(pluginId))
      throw new ArgumentException("'pluginId' is required.", "pluginId");

    PluginId = new Guid(pluginId);
  }

  public Guid PluginId { get; private set; }
}

你不需要使用元数据合同IPluginMetadata来装饰导出属性,因为MEF会自动投影这些属性,但我更喜欢这样做,这样如果我对元数据合同进行更改,那么我的导出属性也应该更新。没有伤害,也没有损失。
完成后,我们可以定义一个抽象基类来实现我们的插件合同:
public abstract class PluginBase : IPlugin
{
  protected PluginBase()
  {
    var attr = GetType()
      .GetCustomAttributes(typeof(ExportPluginAttribute), true)
      .Cast<ExportPluginAttribute>()
      .SingleOrDefault();

    PluginId = (attr == null) ? Guid.Empty : attr.PluginId;
  }

  public virtual Guid PluginId { get; private set; }

  public abstract void Initialise();
}

我们可以通过抽象类的构造函数获取自定义属性,并相应地应用该属性。 我们可以这样做:

public IPlugin GetPlugin(Guid id)
{
  var plugin = container
    .GetExports<IPlugin, IPluginMetadata>()
    .Where(p => p.Metadata.PluginId == id)
    .Select(p => p.Value)
    .FirstOrDefault();

  return plugin;
}

同时还有:

[ExportPlugin("BE112EA1-1AA1-4B92-934A-9EA8B90D622C")]
public class MyPlugin : PluginBase
{
  public override Initialise()
  {
    Console.WriteLine(PluginId);
  }
}

我们可以看到,我们的PluginId通过导出的元数据以及插件的属性暴露出来。
那段代码都没有经过测试,但我希望它能让您朝着正确的方向前进。

1
这就是我想要的,尽管我要指出,在元数据对象的属性类型中不能使用Guid。这不是一个*很大的问题,我只是在使用CodeContract来在属性的构造函数中强制执行它。 - Aaron Powell

0
将GUID放入常量中,并同时用于属性和元数据:
[Export(typeof(IFoo))]
[ExportMetadata("GUID", _guid)]
public class Foo : IFoo
{
    private const string _guid = "abc";

    public string Guid { get { return _guid; } }
}

请注意,您不能使用Guid类型代替string,因为这是不被const关键字允许的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接