MEF和版本控制

时间:2013-02-25 01:24:50

标签: plugins mef composition

我正在考虑使用MEF来解决插件管理要求。在模糊中它表示“没有硬依赖”,但据我所知,导入/导出界面存在严重依赖。

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

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

  1. 编辑界面 - 可能不好,这会破坏现有的插件,因为它们将无法正确实现界面。
  2. 创建一个继承自原始

    的新“V2”界面

    公共接口IPluginV2:IPlugin {}

  3. 现在我遇到了问题。我的用户都有一堆实现IPlugin的第三方插件,但我现在要求他们实现IPluginV2。我认为这些第三方插件将不再有效,直到开发人员实现新界面。

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

2 个答案:

答案 0 :(得分:13)

对于版本控制,您可能需要每个版本的界面,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中它对host参数没有任何作用,但它从 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(); }
}

导入IPluginIPluginV2部分,将每个IPlugin调整为IPluginV2,并公开所有已发现插件的连锁序列。完成调整后,所有插件都可以被视为 V2 插件。

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

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

答案 1 :(得分:1)

只有一些建议(我没有测试过)可能有助于为您解决问题:

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

  2. 如果不使用MEF,来自第三方的动态加载DLL应该返回它已实现的当前界面版本,您可以根据版本号选择可以进行的调用

  3. 这有帮助吗?

    干杯,dimamura