导出零件的 MEF 元数据

发布于 2024-10-03 18:32:09 字数 166 浏览 2 评论 0原文

我希望使用 MEF 作为我正在构建的应用程序的插件系统。我希望每个组件都有一个标识符(GUID),我希望能够对其进行查找。但在处理导出的部件时,此 ID 也很有用。

有没有一种方法可以让我拥有一个包含 ID 以及导出部分的属性(或方法)的元数据属性,而不需要让开发人员填写两次或使用反射从属性中查找它?

I'm looking to use MEF for a plugin system for an application I'm building. Each component I want to have an identifier on (a GUID) which I want to be able to look up against. But this ID is also something that is useful when working with the exported part.

Is there a way that I can have a Metadata attribute which contains the ID as well as a property (or method) on the exported part, short of having developers fill it out twice or use reflection to find it from the attribute?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

万人眼中万个我 2024-10-10 18:32:09

它可能是 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 通过导出的元数据以及我们插件的属性公开。

该代码均未经测试,但我希望它能将您推向正确的方向。

It's likely to be a mixture of a MEF metadata attribute, and an abstract base class. I would define my plugin contract as something like:

public interface IPluginMetadata
{
  Guid PluginId { get; }
}

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

I've enforced that the IPlugin interface also inherits our metadata contract IPluginMetadata. Next, we can create a custom export attribute:

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

You don't need to decorate the export attribute with the metadata contract IPluginMetadata, as MEF will project the properties anyway, but I prefer to do so, so if I do introduce changes to my metadata contract, then my export attribute should be updated too. No harm, no foul.

Once we've done this, we can define an abstract base class from which to implement our plugin contract:

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

We can then grab the custom attribute through the abstract class's constructor, and apply the property accordingly. That we can do:

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

  return plugin;
}

And also:

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

We can see that out PluginId is exposed both through exported metadata, as well as a property of our plugin.

That code is all untested, but I hope it pushes you in the right direction.

心舞飞扬 2024-10-10 18:32:09

将 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 关键字允许。

Put the GUID in a constant, and use it for both a property and the metadata:

[Export(typeof(IFoo))]
[ExportMetadata("GUID", _guid)]
public class Foo : IFoo
{
    private const string _guid = "abc";

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

Note that you can't use the Guid type instead of string, as that is not permitted by the const keyword.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文