MEF:加载具有不同属性的部件(插件)

发布于 2024-07-30 00:24:44 字数 820 浏览 7 评论 0原文

简要背景

我的团队决定使用 Microsoft 的托管可扩展性框架 (MEF) 来提供可扩展模型,以便将新的“提供者”添加到我们的系统中。

这使我们能够相对轻松地插入新的第三方提供商。

注意:MEF 的使用、启动和运行的简单性给我留下了深刻的印象。

我的问题:

由于这些提供程序通常具有不同的属性与它们相关的是,当在运行时将这些提供程序加载到系统中时,我们需要访问提供程序的数据流和属性。

由于属性不同,应采取什么方法才能与所述提供程序插件一起使用? 注意到他们都做着类似的工作。

我的解决方案:

创建一个提供者必须遵守的接口,从而在每个第三方提供者周围创建一个“包装器”,从而产生一致的接口/ 与每个提供商合作的编程模型。

插件=第3方数据源(提供者)+通用接口实现。

+ve: 所述插件不需要更复杂的基于反射的动态“插件”。

-ve: 必须为每个提供者编写一个包装器。 (无论如何,我们都需要添加 MEF 导出标签)

进一步注意:

对我来说,接口/包装器方法将是最简单的,但我被告知要研究基于反射的方法,可能 利用反射来发现运行时可以暴露给系统的属性。

我不赞成任何一种解决方案胜过另一种,但我有兴趣听到社区的想法(其中大多数比我更有经验)。

谢谢。

Brief Background:

My team has decided to use Microsoft's Managed Extensibility Framework (MEF) in order to provide an extensible model for adding new "providers" into our system.

This allows for us to plug in new 3rd party providers with relative ease.

Note: I was impressed by how simple MEF was to use and get up and running with.

My Question:

Since these providers commonly have different properties associated with them, when loading these providers into the system at run-time we need to access the providers data streams and properties.

What approach should be taken in order to work with said provider plug-ins due to the differing properties? Noting they all do a similar job.

My Solution:

Create an interface which the providers must conform to, resulting in a "wrapper" being created around each of the 3rd party providers resulting in a consistent interface /
programming model for working with each provider.

Plug-in = 3rd party data source (provider) + Common interface implementation.

+ve:
No need for a more complex reflection based dynamic "plug" for said plug-ins.

-ve:
Have to write a wrapper for each provider. (We need to add the MEF Export tags regardless)

Further Note:

To me the interface / wrapper approach would be the simplest but I have been told to investigate a reflection based approach which may utilize reflection in order to discover the properties at run-time which can be exposed to the system.

I am not in favor of any one solution over another, but I would be interested in hearing the thoughts of the community (most of which are more experienced than I).

Thanks.

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

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

发布评论

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

评论(3

錯遇了你 2024-08-06 00:24:44

实际上,在预览版 6 中,我们提供了未密封的导出,并允许您创建包含元数据的自定义导出属性,从而消除了零件作者添加单独导出的需要。 我们所有的导入属性也都已解封。

[MetadataAttribute]
[AttributeUsage(AllowMultiple=false)] 
public class RuleAttribute : ExportAttribute {
  public RuleAttribute(string name, string description) {
    Name=name;
    Description=description;

  } : base(typeof(IRule))

  public string Name {get;private set;}
  public string Description {get; private set;}
}

上面的 RuleAttribute 导出 IRule,并且还允许提供 Name 元数据。

用法如下:

[Rule("AddOneRule", "Adds one to the value")]
public class AddOneRule {
}

HTH
格伦

Actually in Preview 6 we have unsealed export and allow you to create custom export attribute which include metadata thus removing the need for part authors to add a separate export. All of our import attributes are also unsealed.

[MetadataAttribute]
[AttributeUsage(AllowMultiple=false)] 
public class RuleAttribute : ExportAttribute {
  public RuleAttribute(string name, string description) {
    Name=name;
    Description=description;

  } : base(typeof(IRule))

  public string Name {get;private set;}
  public string Description {get; private set;}
}

RuleAttribute above exports IRule, and also allows providing Name metadata.

The usage would then be the following:

[Rule("AddOneRule", "Adds one to the value")]
public class AddOneRule {
}

HTH
Glenn

‘画卷フ 2024-08-06 00:24:44

目前还不清楚您在谈论什么“属性”和“数据流”,但仍然如此。

是的,通用接口总是一件好事。 既然您拥有所有这些“属性”等,我建议如下:

interface IProperty
{
    string Name { get; }
    object Value { get; }
}

interface IDataStreamProvider
{
    Stream OpenStream();
}

interface IPlugin
{
    ReadOnlyCollection<IProperty> Properties { get; }

    ReadOnlyCollection<IDataStreamProvider> DataStreams { get; }
}

说到“包装器”:我不明白它们的意图。 所有第三方插件必须实现 IPlugin 接口,并且必须使用 ExportAttributePluginAttribute 进行装饰,如下所示:

class PluginAttribute : ExportAttribute
{
    public PluginAttribute() :
        base(typeof(IPlugin))
    {
    }
}

应尽可能避免反射可能是因为可维护性问题。

It's not very clear what "properties" and "data streams" are you talking about, but still.

Yes, common interface is always a good thing. And since you have all these "properties" and such, I suggest the following:

interface IProperty
{
    string Name { get; }
    object Value { get; }
}

interface IDataStreamProvider
{
    Stream OpenStream();
}

interface IPlugin
{
    ReadOnlyCollection<IProperty> Properties { get; }

    ReadOnlyCollection<IDataStreamProvider> DataStreams { get; }
}

Speaking of "wrappers": I do not understand the intent of those. All third-party plugins must implement IPlugin interface and must be decorated with either ExportAttribute or PluginAttribute as in this:

class PluginAttribute : ExportAttribute
{
    public PluginAttribute() :
        base(typeof(IPlugin))
    {
    }
}

Reflection should be avoided as much as possible because of maintainability concerns.

っ左 2024-08-06 00:24:44

我为添加此类信息所做的工作是为插件创建一些自定义属性,然后在加载插件时使用 MEF 读取这些属性。 您可以将任何内容添加到属性类中,例如名称、枚举、整数、其他字符串,并且它非常易于使用。 但请注意,新的预览版 6 确实改变了这些处理方式的一些内容。

[MetadataAttribute]
public class MyMetadataAttribute : Attribute
{
    public MyType MyUsage { get; set; }
}

public interface IMyMetadataView
{
    MyType MyUsage { get; }
}

public enum MyType
{
    Undefined,
    TypeOne,
    TypeTwo
}

然后在插件中你可以像这样定义它......

[Export(typeof(IMyInterface))]
[MyMetadataAttribute(MyUsage = MyType.TypeOne)]
public class PluginClass: IMyInterface
{
}

你需要将东西添加到导入中然后

[ImportMany(AllowRecomposition = true)]
public IEnumerable<Lazy<IMyInterface, IMyMetadataView>> plugins { get; set; }

你也可以直接使用每个插件的数据

var typeOnePlugin = plugins.FirstOrDefault(p => p.Metadata.MyUsage == MyType.TypeOne);

这也是使用 7 月份发布的预览版 6 的方式。

What I have done for adding info like this is to make some custom attributes for the plugins and then read those in with MEF when the plugins get loaded. You can add anything into the attribute class, like names, enums, ints, other strings, and it is pretty easy to use. But be careful, the new preview6 did change a few things in how those are handled.

[MetadataAttribute]
public class MyMetadataAttribute : Attribute
{
    public MyType MyUsage { get; set; }
}

public interface IMyMetadataView
{
    MyType MyUsage { get; }
}

public enum MyType
{
    Undefined,
    TypeOne,
    TypeTwo
}

And then in the plugin you can define it like this...

[Export(typeof(IMyInterface))]
[MyMetadataAttribute(MyUsage = MyType.TypeOne)]
public class PluginClass: IMyInterface
{
}

You need to add things to the import then also

[ImportMany(AllowRecomposition = true)]
public IEnumerable<Lazy<IMyInterface, IMyMetadataView>> plugins { get; set; }

You can then use the data directly for each plugin

var typeOnePlugin = plugins.FirstOrDefault(p => p.Metadata.MyUsage == MyType.TypeOne);

Again this is the way using preview 6 that came out in July.

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