C# MEF错误,是循环依赖,现在是其他的

C# MEF错误,是循环依赖,现在是其他的,c#,.net-3.5,inversion-of-control,mef,circular-dependency,C#,.net 3.5,Inversion Of Control,Mef,Circular Dependency,由于我的应用程序架构发生了变化,我最近遇到了一个循环依赖 该应用程序依赖于通过MEF加载插件的插件管理器。之前的一切都很好,因为它看起来像这样: // model.cs [Export("Model")] public class Model { public PluginManager PM { get; set; } [ImportingConstructor] public Model( [Import] PluginManager plugin_manager) {

由于我的应用程序架构发生了变化,我最近遇到了一个循环依赖

该应用程序依赖于通过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
{
}
// 1st possible implementation
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
  [Import(typeof(PluginQueryInterface))]
  public PluginQueryInterface QueryInterface { get; set; }

  public MyPlugin() {}
}
但是现在我有一种情况,我希望所有插件都能够通过接口查询PluginManager(或者任何其他对象),以了解系统中的其他插件,从而了解它们的功能。我通过添加另一个接口“解决”了这个问题,我们称之为PluginQueryInterface。然后,我让模型实现了这个接口

[Export("Model"))]
[Export(typeof(PluginQueryInterface))]
public class Model : PluginQueryInterface
{
  // same as before
}
然后插件签名将如下所示:

// 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
{
}
// 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
  }
}
2nd实现显然是一个循环引用,因为插件要求在创建插件之前创建PluginQueryInterface,但PluginQueryInterface是模型,它必须导入PluginManager,而PluginManager又需要创建所有的plugininterface。。。当我启动时,我确实得到了一个MEF循环依赖错误

1st实现对我来说似乎不是循环引用。如果PluginQueryInterface是一个属性,那么我认为它在被使用之前不会被解析。构造函数根本不使用它。那么为什么PluginManager不能愉快地创建我所有的MyPlugin呢?在这两种情况下,我得到相同的MEF错误

我试图通过让PluginManager实现PluginQueryInterface来解决这个问题,因为a)它无论如何都是有意义的,b)它是a--让两个相互依赖的类依赖于第三个类。现在的问题是,我得到了一个不同的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错误。这几乎就像它不希望我加载多个带有特定接口的插件一样。。。但我使用的是ImportMany,这难道不会表现为某种类型的
基数异常吗

更新

我不理解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设计师不想让任何人做的事情。我可以盲目地注释掉
这个,但这不可能是正确的。我的猜测是,这将归结为我使用的MEF属性,但由于导入此值的插件的创建策略为
CreationPolicy.Shared
,为什么我会有问题?

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

此外,这可能是线程问题。您应该尝试将容器构造为线程安全的:


我将查看threadsafe标志。我甚至没有意识到有一个!