我正在考虑使用MEF解决插件管理要求。在简介中,它表示“没有硬性依赖关系”,但据我所知,导入/导出接口(interface)上存在硬性依赖关系。
我担心的是这个。我的可扩展应用程序是由我编写的。插件是由第三方编写的。因此,可以说我们都是从V1开始的。我的应用程序定义了插件“parts”需要实现的IPlugin
接口(interface)。我们部署了该应用程序,并且用户安装了许多第三方插件。一切都很好。
现在,我升级了我的应用程序,并想向插件界面添加新方法。我看到的方式有2种选择:
公用接口(interface)IPluginV2:IPlugin {}
现在我有一个问题。我的用户都有很多实现
IPlugin
的第三方插件,但是我现在要求他们实现IPluginV2。我认为在开发人员实现新接口(interface)之前,这些第3方插件将不再起作用。MEF是否有办法处理这种情况?我真的在寻找一种方法,让我可以在不重新构建旧插件的情况下继续开发自己的应用程序。最好的处理方法是什么?
最佳答案
对于版本控制,您可能希望每个版本都有一个接口(interface),并且adapter pattern介于它们之间。这是System.AddIn
处理版本控制的方式,它也适用于MEF。
假设您的应用程序的 V1 具有以下类型:
public interface IPlugin
{
string Name { get; }
string Publisher { get; }
string Version { get; }
void Init();
}
这是我们支持 V1 插件的应用程序的唯一契约(Contract)。它包含在程序集
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(); }
}
它将导入在“插件”文件夹中找到的所有
IPlugin
部分。然后,我们决定发布 V2 ,由于我们要提供版本控制,因此我们需要无版本的契约(Contract):
public interface IPluginV2
{
string Name { get; }
string Publisher { get; }
string Version { get; }
string Description { get; }
void Init(IHost host);
}
具有新属性和修改后的方法签名。另外,我们为主机添加了一个接口(interface):
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 契约(Contract)中调用了无参数的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,它可以使用MEF进行integrate并可以使用adapters支持版本控制。
关于plugins - MEF和版本控制,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15058797/