我正在考虑使用MEF来解决插件管理要求。在模糊中它表示“没有硬依赖”,但据我所知,导入/导出界面存在严重依赖。
我担心的是这个。我的可扩展应用程序是由我编写的。插件由第三方编写。所以我们都说从V1开始。我的应用定义了插件“部件”需要实现的IPlugin
接口。我们部署应用程序,用户安装了一堆第三方插件。一切都很好。
现在我升级我的应用程序,我想在插件界面中添加一个新方法。我看到它的方式我有两个选择:
创建一个继承自原始
的新“V2”界面公共接口IPluginV2:IPlugin {}
现在我遇到了问题。我的用户都有一堆实现IPlugin
的第三方插件,但我现在要求他们实现IPluginV2。我认为这些第三方插件将不再有效,直到开发人员实现新界面。
MEF有办法处理这种情况吗?我真的在寻找一种让我可以改进我的应用程序的方法,同时让旧的插件继续工作而不必重建。什么是最好的处理方式?
答案 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(); }
}
导入IPlugin
和IPluginV2
部分,将每个IPlugin
调整为IPluginV2
,并公开所有已发现插件的连锁序列。完成调整后,所有插件都可以被视为 V2 插件。
您还可以在主机界面上使用适配器模式,以允许 V2 插件与 V1 主机配合使用。
答案 1 :(得分:1)
只有一些建议(我没有测试过)可能有助于为您解决问题:
如果使用MEF,请为每个版本使用不同的AggregateCatalog
。这样你可以维护V1和V2插件
如果不使用MEF,来自第三方的动态加载DLL应该返回它已实现的当前界面版本,您可以根据版本号选择可以进行的调用
这有帮助吗?
干杯,dimamura