Plugins MEF和版本控制

Plugins MEF和版本控制,plugins,mef,composition,Plugins,Mef,Composition,我正在考虑使用MEF来解决插件管理需求。在简介中说“没有硬依赖项”,但就我所见,在导入/导出接口上存在硬依赖项 我关心的是这一点。我的可扩展应用程序是我写的。插件由第三方编写。假设我们都从V1开始。我的应用程序定义了插件“部件”需要实现的IPlugin接口。我们部署应用程序,用户安装一堆第三方插件。一切都很好 现在我升级了我的应用程序,我想给插件界面添加一个新方法。在我看来,我有两个选择: 编辑接口-可能不好,这会破坏现有插件,因为它们不再正确实现接口 创建一个新的“V2”接口,该接口继承自原始

我正在考虑使用MEF来解决插件管理需求。在简介中说“没有硬依赖项”,但就我所见,在导入/导出接口上存在硬依赖项

我关心的是这一点。我的可扩展应用程序是我写的。插件由第三方编写。假设我们都从V1开始。我的应用程序定义了插件“部件”需要实现的
IPlugin
接口。我们部署应用程序,用户安装一堆第三方插件。一切都很好

现在我升级了我的应用程序,我想给插件界面添加一个新方法。在我看来,我有两个选择:

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

    公共接口IPluginV2:IPlugin{}

  • 现在我有一个问题。我的用户都有很多第三方插件实现了
    IPlugin
    ,但我现在要求他们实现IPluginV2。我认为这些第三方插件将不再工作,直到开发人员实现新的接口


    MEF有办法处理这种情况吗?我真的在寻找一种方法,让我在不需要重新构建的情况下让旧插件继续工作的同时改进我的应用程序。处理这一问题的最佳方法是什么?

    只有几条建议(我还没有测试过),可以帮助您集思广益地找到解决方案:

  • 如果使用MEF,请对每个版本使用不同的
    AggregateCatalog
    。这样您就可以同时维护V1和V2插件

  • 如果不使用MEF,则来自第三方的动态加载DLL应返回其已实现的当前接口版本,并且您可以根据版本号选择可以进行的调用

  • 那有用吗


    干杯,dimamura

    对于版本控制,您可能希望每个版本都有一个接口,并在它们之间进行切换。这就是
    System.AddIn
    处理版本控制的方式,它也适用于MEF

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

    public interface IPlugin
    {
        string Name { get; }
        string Publisher { get; }
        string Version { get; }
    
        void Init();
    }
    
    这是我们的V1插件感知应用程序的唯一合同。它包含在assembly
    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
    。它包含在assembly
    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(); }
    }
    
    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(); }
    }
    
    使用新属性和修改的方法签名。此外,我们还为主机添加了一个接口:

    public interface IHost
    {
        //Here we can add something useful for a plugin.
    }
    
    这两项都包含在assembly
    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
    中,它不处理主机参数,但从V1契约调用无参数
    Init

    最后是V2主机:

    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(); }
    }
    
    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(); }
    }
    
    class HostV2WithVersioning:IHost,IDisposable
    {
    合成容器_容器;
    [进口数量(类型(IPluginV2))]
    IEnumerable\u pluginsV2;
    [进口数量(类型(IPlugin))]
    i可数插件v1;
    公共IEnumerable插件
    {
    得到
    {
    返回_pluginsV1.Select(p1=>newv1tov2pluginadapter(p1)).Concat(_pluginsV2);
    }
    }
    具有版本控制()的公共主机v2
    {
    var catalog=新目录目录(“插件”);
    _容器=新的合成容器(目录);
    _容器。组件(本);
    }
    public void Dispose(){u container.Dispose();}
    }
    
    它同时导入
    IPlugin
    IPluginV2
    部分,将每个
    IPlugin
    适配到
    IPluginV2
    中,并公开所有已发现插件的串联序列。适应完成后,所有插件都可以被视为V2插件

    您还可以使用主机接口上的适配器模式来允许V2插件与V1主机一起工作


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

    感谢您不厌其烦地发布一个全面的答案。很抱歉,我花了这么长时间才将此标记为可接受的答案,但我想晚做总比不做强;-)