MEF和版本控制

Tim*_*ong 7 plugins mef composition

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

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

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

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

    公共接口IPluginV2:IPlugin {}

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

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

Pan*_*nis 13

对于版本控制,您可能希望每个版本的接口和适配器模式在它们之间.它是如何System.AddIn处理版本控制的,它也适用于MEF.

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

public interface IPlugin
{
    string Name { get; }
    string Publisher { get; }
    string Version { get; }

    void Init();
}
Run Code Online (Sandbox Code Playgroud)

这是我们的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个导出为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(); }
}
Run Code Online (Sandbox Code Playgroud)

导入IPlugin文件夹"plugins"中的所有部件.

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

public interface IPluginV2
{
    string Name { get; }
    string Publisher { get; }
    string Version { get; }
    string Description { get; }

    void Init(IHost host);
}
Run Code Online (Sandbox Code Playgroud)

使用新属性和修改后的方法签名.另外,我们为主机添加了一个界面:

public interface IHost
{
    //Here we can add something useful for a plugin.
}
Run Code Online (Sandbox Code Playgroud)

这两个都包含在程序集中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(); }
}
Run Code Online (Sandbox Code Playgroud)

这简单地适应IPluginIPluginV2.它返回一个固定的描述,并在Init它没有采取任何与宿主的说法,但它调用的参数的InitV1的合同.

最后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(); }
}
Run Code Online (Sandbox Code Playgroud)

这两者都导入IPluginIPluginV2零件,以适应每个IPlugin进入IPluginV2 ,并公开所有发现的插件连接序列.完成调整后,所有插件都可以视为V2插件.

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

另一种方法是autofac IoC,它可以与MEF 集成,并且可以使用适配器支持版本控制.