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

时间:2009-07-31 14:07:25

标签: reflection plugins c#-3.0 interface mef

简要背景:

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

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

注意:我对MEF的使用和启动运行方式印象深刻。

我的问题:

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

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

我的解决方案:

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

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

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

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

进一步说明:

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

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

感谢。

3 个答案:

答案 0 :(得分:2)

实际上,在预览版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 格伦

答案 1 :(得分:1)

目前尚不清楚你所谈论的“属性”和“数据流”是什么,但仍然存在。

是的,通用界面总是好事。既然你拥有所有这些“属性”等,我建议如下:

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

由于可维护性问题,应尽可能避免反思。

答案 2 :(得分:1)

我为添加这样的信息所做的是为插件制作一些自定义属性,然后在插件加载时用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的方式。