代码之家  ›  专栏  ›  技术社区  ›  holsee

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

  •  2
  • holsee  · 技术社区  · 16 年前

    简要背景:

    我的团队决定使用微软的托管扩展框架( MEF )为了向我们的系统中添加新的“提供者”,提供一个可扩展的模型。

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

    注:我对MEF的使用和运行方式非常简单印象深刻。

    我的问题:

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

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

    我的解决方案:

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

    插件=第三方数据源(提供者)+公共接口实现。

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

    -VE: 必须为每个提供程序编写包装。(不管怎样,我们都需要添加MEF导出标记)

    进一步说明:

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

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

    谢谢。

    3 回复  |  直到 15 年前
        1
  •  1
  •   Anton Gogolev    16 年前

    现在还不清楚你在说什么“属性”和“数据流”,但仍然不清楚。

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

    interface IProperty
    {
        string Name { get; }
        object Value { get; }
    }
    
    interface IDataStreamProvider
    {
        Stream OpenStream();
    }
    
    interface IPlugin
    {
        ReadOnlyCollection<IProperty> Properties { get; }
    
        ReadOnlyCollection<IDataStreamProvider> DataStreams { get; }
    }
    

    说到“包装纸”:我不明白它们的意图。所有第三方插件必须实现 IPlugin 接口,必须用 ExportAttribute PluginAttribute 像这样:

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

    考虑到可维护性,应尽可能避免反射。

        2
  •  2
  •   Glenn Block    15 年前

    实际上,在预览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,还允许提供名称元数据。

    使用方法如下:

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

    高温高压 格伦

        3
  •  1
  •   SteveM    16 年前

    我为添加这样的信息所做的就是为插件创建一些自定义属性,然后在加载插件时用mef读取这些属性。您可以在属性类中添加任何内容,如名称、枚举、int和其他字符串,而且非常容易使用。但是要小心,新的预览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的方法。