2010-09-12 16 views
4

我有一个循环依赖关系,最近由于我的应用程序体系结构发生了变化。MEF错误,是循环依赖关系,现在是别的东西

该应用程序依赖于通过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,但t他的PluginQueryInterface是模型,它必须导入PluginManager,而后者需要创建所有的PluginInterfaces ...并且在启动时确实会得到MEF循环依赖关系错误。

第1实现似乎不像一个循环引用给我。如果PluginQueryInterface是一个属性,那么我认为它不会被解决,直到它被使用。它并没有被构造函数使用。那为什么PluginManager不能快速创建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

UPDATE

我不明白,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,为什么我会遇到问题?

回答

3

嗯,我有一个可能的解决方案。我没有任何使用这方面的经验,但使用Lazy实例化似乎有帮助。至少我可以继续前进,而不必更改我不完全理解的MEF代码。