c# - MEF 错误,是循环依赖,现在是其他东西

标签 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)
  {
    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
{
}

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

[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
  }
}

2 实现很明显是一个循环引用,因为插件要求在创建插件之前创建 PluginQueryInterface,但是 PluginQueryInterface 是模型,它必须导入 PluginManager,它在turn 需要创建的所有 PluginInterfaces... 我在启动时确实遇到了 MEF 循环依赖错误。

1st 实现对我来说不像是循环引用。如果 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 错误。就好像它不希望我加载多个具有特定接口(interface)的插件...但我正在使用 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 的创建策略,为什么我会遇到问题?

最佳答案

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

关于c# - MEF 错误,是循环依赖,现在是其他东西,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3696893/

相关文章:

frameworks - 使用 Entity Framework 和 CaSTLe Windsor 容器解决通用存储库的问题

c# - 在 Unity3D C# 中使用曲线分割纹理

string - 使用 .NET 3.5 查找字符串中的第一个数字

dependency-injection - 带有选定引号的控制反转与依赖注入(inject)——我的理解是否正确?

asp.net-mvc - ASP.NET MVC & Windsor.CaSTLe : working with HttpContext-dependent services

c# - 从字符串中删除最后一个字符并开始特定字符

c# - 在WAV记录中读取通过 radio 发送的二进制代码

c# - 在单独的线程上创建 WebBrowser 控件

c# - 在不实现任何自定义序列化/反序列化时是否需要实现 ISerializable 接口(interface)

c# - 使用带有参数的 Oracle 函数将记录集从 Oracle 返回到 .Net