我正在考虑使用MEF解决插件管理要求。在简介中,它表示“没有硬性依赖关系”,但据我所知,导入/导出接口(interface)上存在硬性依赖关系。

我担心的是这个。我的可扩展应用程序是由我编写的。插件是由第三方编写的。因此,可以说我们都是从V1开始的。我的应用程序定义了插件“parts”需要实现的IPlugin接口(interface)。我们部署了该应用程序,并且用户安装了许多第三方插件。一切都很好。

现在,我升级了我的应用程序,并想向插件界面添加新方法。我看到的方式有2种选择:

  • 编辑接口(interface)-可能不好,这将破坏现有的插件,因为它们将不再正确实现该接口(interface)。
  • 创建一个新的“V2”接口(interface),该接口(interface)继承了原始接口(interface)

    公用接口(interface)IPluginV2:IPlugin {}

  • 现在我有一个问题。我的用户都有很多实现IPlugin的第三方插件,但是我现在要求他们实现IPluginV2。我认为在开发人员实现新接口(interface)之前,这些第3方插件将不再起作用。

    MEF是否有办法处理这种情况?我真的在寻找一种方法,让我可以在不重新构建旧插件的情况下继续开发自己的应用程序。最好的处理方法是什么?

    最佳答案

    对于版本控制,您可能希望每个版本都有一个接口(interface),并且adapter pattern介于它们之间。这是System.AddIn处理版本控制的方式,它也适用于MEF。

    假设您的应用程序的 V1 具有以下类型:

    public interface IPlugin
    {
        string Name { get; }
        string Publisher { get; }
        string Version { get; }
    
        void Init();
    }
    

    这是我们支持 V1 插件的应用程序的唯一契约(Contract)。它包含在程序集Contracts.v1中。

    然后我们有一个 V1 插件:
    [Export(typeof(IPlugin))]
    public class SomePlugin : IPlugin
    {
        public string Name { get { return "Some Plugin"; } }
    
        public string Publisher { get { return "Publisher A"; } }
    
        public string Version { get { return "1.0.0.0"; } }
    
        public void Init() { }
    
        public override string ToString()
        {
            return string.Format("{0} v.{1} from {2}", Name, Version, Publisher);
        }
    }
    

    导出为IPlugin。它包含在程序集Plugin.v1中,并发布在主机的应用程序基本路径下的“plugins”文件夹中。

    最后是 V1 主机:
    class Host : IDisposable
    {
        CompositionContainer _container;
    
        [ImportMany(typeof(IPlugin))]
        public IEnumerable<IPlugin> Plugins { get; private set; }
    
        public Host()
        {
            var catalog = new DirectoryCatalog("plugins");
            _container = new CompositionContainer(catalog);
            _container.ComposeParts(this);
        }
    
        public void Dispose() { _container.Dispose(); }
    }
    

    它将导入在“插件”文件夹中找到的所有IPlugin部分。

    然后,我们决定发布 V2 ,由于我们要提供版本控制,因此我们需要无版本的契约(Contract):
    public interface IPluginV2
    {
        string Name { get; }
        string Publisher { get; }
        string Version { get; }
        string Description { get; }
    
        void Init(IHost host);
    }
    

    具有新属性和修改后的方法签名。另外,我们为主机添加了一个接口(interface):
    public interface IHost
    {
        //Here we can add something useful for a plugin.
    }
    

    这两个都包含在程序集Contracts.v2中。

    为了允许版本控制,我们将插件适配器从V1添加到V2:
    class V1toV2PluginAdapter : IPluginV2
    {
        IPlugin _plugin;
    
        public string Name { get { return _plugin.Name; } }
    
        public string Publisher { get { return _plugin.Publisher; } }
    
        public string Version { get { return _plugin.Version; } }
    
        public string Description { get { return "No description"; } }
    
        public V1toV2PluginAdapter(IPlugin plugin)
        {
            if (plugin == null) throw new ArgumentNullException("plugin");
            _plugin = plugin;
        }
    
        public void Init(IHost host) { plugin.Init(); }
    
        public override string ToString() { return _plugin.ToString(); }
    }
    

    这只是从IPlugin适应IPluginV2。它返回一个固定的描述,并且在Init中,它对host参数没有任何作用,但是它从 V1 契约(Contract)中调用了无参数的Init

    最后是 V2 主机:
    class HostV2WithVersioning : IHost, IDisposable
    {
        CompositionContainer _container;
    
        [ImportMany(typeof(IPluginV2))]
        IEnumerable<IPluginV2> _pluginsV2;
    
        [ImportMany(typeof(IPlugin))]
        IEnumerable<IPlugin> _pluginsV1;
    
        public IEnumerable<IPluginV2> Plugins
        {
            get
            {
                return _pluginsV1.Select(p1 => new V1toV2PluginAdapter(p1)).Concat(_pluginsV2);
            }
        }
    
        public HostV2WithVersioning()
        {
            var catalog = new DirectoryCatalog("plugins");
            _container = new CompositionContainer(catalog);
            _container.ComposeParts(this);
        }
    
        public void Dispose() { _container.Dispose(); }
    }
    

    它同时导入了IPluginIPluginV2部分,将每个IPlugin修改为IPluginV2并公开了所有发现的插件的串联序列。修改完成后,所有插件都可以视为 V2 插件。

    您还可以在主机界面上使用适配器模式,以允许 V2 插件与 V1 主机一起使用。

    另一种方法是autofac IoC,它可以使用MEF进行integrate并可以使用adapters支持版本控制。

    关于plugins - MEF和版本控制,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15058797/

    10-13 05:29