由于我的应用程序架构发生了变化,我最近出现了循环依赖。
该应用程序依赖于通过MEF加载插件的插件管理器。一切都工作正常,因为它看起来像这样:
// model.cs
[Export("Model")]
public class Model
{
public PluginManager PM { get; set; }
[ImportingConstructor]
public Model( [Import] PluginManager plugin_manager)
{
PM = plugin_manager;
}
}
// pluginmanager.cs
[Export(typeof(PluginManager))]
public class PluginManager
{
[ImportMany(typeof(PluginInterface))]
private IEnumerable<PluginInterface> Plugins { get; set; }
}
插件看起来像这样:
// myplugin.cs
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
}
但现在我的情况是,我希望所有插件都能够通过接口查询PluginManager(或可能是任何其他对象),以了解系统中的其他插件以了解其功能。我通过添加另一个接口“解决”了这个问题,我们称之为PluginQueryInterface。然后我让模型实现了这个界面。
[Export("Model"))]
[Export(typeof(PluginQueryInterface))]
public class Model : PluginQueryInterface
{
// same as before
}
然后插件签名看起来像这样:
// 1st possible implementation
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
[Import(typeof(PluginQueryInterface))]
public PluginQueryInterface QueryInterface { get; set; }
public MyPlugin() {}
}
或者
// 2nd possible implementation
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
private PluginQueryInterface QueryInterface { get; set; }
[ImportingConstructor]
public MyPlugin( [Import] PluginQueryInterface query_interface)
{
QueryInterface = query_interface
}
}
第二实现显然是一个循环引用,因为插件要求在创建插件之前创建PluginQueryInterface,但PluginQueryInterface是Model,它必须导入PluginManager,它在转向需要创建的所有PluginInterfaces ...当我启动时,我确实得到了MEF循环依赖性错误。
第一次实施似乎不是对我的循环引用。如果PluginQueryInterface是一个属性,那么我认为它不会被解析直到它被使用。并且构造函数根本不使用它。那么为什么插件管理器不能快乐地创建我的所有MyPlugins呢?在这两种情况下我都得到了相同的MEF错误。
我试图通过使PluginManager实现PluginQueryInterface来解决这个问题,因为a)它无论如何都是有道理的b)它是known way of dealing with circular dependencies - 使两个相互依赖的类取而代之的是第三个类。现在的问题是我得到了一个不同的MEF错误!这就是它所说的:
GetExportedValue cannot be called before prerequisite import 'Company.App.PluginManager..ctor(Parameter="database_filepath", ContractName="PluginManager.filename")' has been set.
WTF?我在代码中设置了断点,并在调用GetExportedValue之前设置了导出值PluginManager.filename
。
我完全难过了。任何观察或建议现在都将非常感激。我试图调试这个问题几个小时,我一直在撞着MEF覆盖的墙壁。
(更新)
我之前没有想到这一点,但它可能是插件之间的差异,所以我删除了两个插件中的一个,现在我的应用程序加载没有MEF错误。我把它添加回来了,它又失败了。然后我删除了另一个插件,它工作了。所以看起来这是其他一些MEF错误。这几乎就好像它不希望我用一个特定的界面加载多个插件......但是我使用的是ImportMany,并且它不会表现为某种CardinalityException
吗?
更新
我不明白MEF的这一部分,希望这里有人可以解释它的全部内容。在进入代码一段时间之后,我发现我的错误源于MEF在找到值后删除导入定义!
private bool TryGetImportValue(ImportDefinition definition, out object value)
{
lock (this._lock)
{
if (this._importValues.TryGetValue(definition, out value))
{
this._importValues.Remove(definition); // this is the line that got me
return true;
}
}
value = null;
return false;
}
我以前从来没有遇到过这个问题,坦率地说,我很难理解我现在正在做什么,我的进口和出口使这个问题浮出水面。我认为我正在做一些MEF设计师并不打算让任何人去做的事情。我可以盲目地评论this._importValues.Remove(definition);
,但这可能不对。我的猜测是,这将归结为我使用的MEF属性,但由于导入此值的插件的创建策略为CreationPolicy.Shared
,为什么我会遇到问题?
答案 0 :(得分:3)
好吧,我有一个可能的解决方案。我没有任何使用它的经验,但使用Lazy
实例化似乎有所帮助。至少我可以继续前进,而不必改变我不完全理解的MEF代码。
答案 1 :(得分:3)
此外,这可能是一个线程问题。您应该尝试将容器构造为ThreadSafe: