【问题标题】:MEF and VersioningMEF 和版本控制
【发布时间】:2013-02-10 02:44:33
【问题描述】:

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

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

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

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

    公共接口 IPluginV2 : IPlugin {}

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

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

【问题讨论】:

    标签: plugins mef composition


    【解决方案1】:

    只是一些建议(我尚未测试过)可能有助于为您集思广益的解决方案:

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

    2. 如果不使用 MEF,则从第三方动态加载的 DLL 应返回它已实现的当前接口版本,您可以根据版本号选择可以进行哪些调用

    这有帮助吗?

    干杯,二村

    【讨论】:

      【解决方案2】:

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

      假设我们为您的应用程序的 V1 提供以下类型:

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

      这是我们的 V1 插件感知应用的唯一合同。它包含在程序集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(); }
      }
      

      它会导入文件夹“plugins”中的所有IPlugin 部件。

      然后我们决定发布 V2,因为我们想要提供版本控制,所以我们需要无版本合约:

      public interface IPluginV2
      {
          string Name { get; }
          string Publisher { get; }
          string Version { get; }
          string Description { get; }
      
          void Init(IHost host);
      }
      

      具有新属性和修改的方法签名。另外我们为主机添加了一个接口:

      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 中它对主机参数不做任何事情,但它从 V1 合约中调用无参数的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(); }
      }
      

      它同时导入IPlugin 和IPluginV2 部分,将每个IPlugin 调整为IPluginV2 并公开所有已发现插件的串联序列。适配完成后,所有插件均可视为V2插件。

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

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

      【讨论】:

      • 感谢您不厌其烦地发布全面的答案。很抱歉,我花了这么长时间才将其标记为已接受的答案,但我猜迟到总比没有好;-)
      猜你喜欢
      • 2011-06-25
      • 1970-01-01
      • 2010-10-01
      • 2010-10-02
      • 1970-01-01
      • 2022-07-05
      • 2018-02-03
      • 2016-11-07
      • 2011-01-21
      相关资源
      最近更新 更多