具有前向和后置的插件架构向后兼容性

时间:2013-03-15 23:11:10

标签: c# plugins backwards-compatibility forward-compatibility

我目前正在开发一种使用插件类型系统的C#产品。这不是什么新鲜事,我已经看到很多关于如何使用接口来轻松实现这个功能的信息。

我还看到了通过更新接口名称来实现向后兼容性的方法,例如:Interface change between versions - how to manage?

对于主要exe和插件之间的版本不匹配,我可以预见多种情况。

  1. 主程序与插件相同的插件版本
  2. 比插件更新的主程序
  3. 比插件旧的主程序
  4. 从我能够收集的信息1& 2工作得很好。但是我还没弄清楚如何正确地正确实现“前向”兼容性(3)。

    我们打算只对插件API添加ADD方法。

    任何想法都会有很大的帮助。

2 个答案:

答案 0 :(得分:2)

隔离的PluginAPI DLL

首先,您的PluginAPI(包含接口)应该是主应用程序的单独DLL。您的主应用程序将引用PluginAPI,每个插件将引用PluginAPI。你很有可能已经这样做了。

界面版本控制

其次,从结构上讲,每次添加新属性或方法时都应创建一个新接口。

例如:

  1. 版本1:Plugins.IPerson
  2. 版本2:Plugins.V2.IPerson:Plugins.IPerson
  3. 版本3:Plugins.V3.IPerson:Plugins.V2.IPerson
  4. 在极少数情况下,您决定删除或完全重新设计API,例如:

    1. 版本4:Plugins.V4.IPerson //没有任何接口继承
    2. 隔离的PluginAPI DLL版本控制

      最后,我并不是100%确定PluginAPI .dll的版本化将如何进行,即使使用接口版本控制的结构架构也是如此。它可能有用

      OR

      您可能需要为每个版本配备匹配的dll(每个版本都引用以前的版本)。我们假设情况就是这样。

      案例3的解决方案

      现在让我们来看看你的案例[3],主程序比插件更早:

      • Person Plugin实现了Plugins.V2.IPlugin并引用了V3 .dll(只是为了让它变得有趣)。
      • 主程序引用V1 .dll
      • 插件文件夹将包含V2和V3插件.dll
      • 主app文件夹只包含V1插件.dll(以及其他文件)
      • 主应用程序将通过IPerson界面的V1定义找到并加载Person插件和引用
      • 当然,只有V1方法和属性可以从插件访问主应用程序
      • (其他方法可以通过反思访问 - 不是你想要的)

      奖金更新

      何时不使用插件

      这不是详尽无遗的,但我是少人的倡导者。如果您正在构建只有您将构建的组件化软件,或者即使您计划向第三方开放,也不要将其作为插件架构,您可以随后添加该功能。让它先工作并尽早发布。类,命名空间和解决方案文件夹为您提供了大量的结构来管理您的项目。您甚至不需要接口,您可以抽象到接口并在以后创建插件架构。

      热插拔插件

      在设计插件架构时,您可能有兴趣知道可以使插件热插拔:

      1. 没有释放内存 - 只需继续加载新插件。这通常很好,除非它可能是一个服务器软件,你期望i)运行很长一段时间而不重新启动; AND ii)期望在此期间进行许多插件更改和升级。在运行时加载插件时,它会将程序集加载到内存中,无法卸载。有关原因,请参见[2]。
      2. 使用释放内存 - 您可以卸载AppDomain。 AppDomains在相同的进程中运行但是引用是隔离的 - 您无法直接引用或调用对象。相反,必须对调用进行编组,并且必须在appdomains之间序列化数据。如果您不打算经常更换插件,增加的复杂性是不值得的:i)由于编组/序列化导致的性能损失,ii)更多的编码复杂性(您不能简单地使用事件和委托和方法正常),iii)这一切都会导致更多的错误,并使调试更加困难。
      3. 因此,如果选项[2]引诱您,请首先尝试[1]并使用该架构,直到您遇到[2]所需的问题。永远不要过度建筑。相信我,我在大学之前建立了一个[2]架构,很有趣,但在大多数情况下过度杀戮并且可能会扼杀你的项目(在非商业功能上花费太多时间)。

答案 1 :(得分:0)

您需要假设您的插件仅实现暴露的接口。如果您使用新界面发布主程序的新版本,您将检查您的插件是否支持该界面。因此,如果将新插件呈现给旧版本的main。它将支持所请求的接口,或者不支持并且将作为有效插件的测试失败。