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

发布于 2024-09-19 01:28:49 字数 3644 浏览 4 评论 0原文

由于我的应用程序架构发生变化,我最近出现了循环依赖关系。

该应用程序依赖于通过 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
{
}

但现在我遇到了一种情况,我希望所有插件都能够通过界面来了解系​​统中的其他插件,以了解它们的功能。我通过添加另一个接口“解决”了这个问题,我们称之为 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() {}
}

or this

// 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,而后者又需要创建所有的 PluginInterfaces...并且在启动时我确实遇到了 MEF 循环依赖错误。

对我来说,第一个实现看起来不像是循环引用。如果 PluginQueryInterface 是一个属性,那么我认为它不会被解析直到它被使用。并且构造函数根本不使用它。那么为什么 PluginManager 不愉快地创建我的所有 MyPlugins 呢?在这两种情况下我都会遇到相同的 MEF 错误。

我试图通过让 PluginManager 实现 PluginQueryInterface 来解决这个问题,因为 a) 无论如何它都是有意义的 b) 它是一个 处理循环依赖的已知方法 - 使两个相互依赖的类依赖于第三个类。现在的问题是我收到了不同的 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 创建策略,为什么我会遇到问题?

I've got a circular dependency that recently came about because of a change in my application architecture.

The application relies on a plugin manager that loads plugins via MEF. Everything up until worked fine, because it looked something like this:

// 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; }
}

and the plugins looked like this:

// myplugin.cs
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
}

But now I've got a situation where I want all plugins to have the ability to query the PluginManager (or possibly any other object) via an interface to find out about other plugins in the system to find out about their capabilities. I "solved" this by adding another interface, let's call it PluginQueryInterface. I then had the Model implement this interface.

[Export("Model"))]
[Export(typeof(PluginQueryInterface))]
public class Model : PluginQueryInterface
{
  // same as before
}

and then the plugin signature would look like this:

// 1st possible implementation
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
  [Import(typeof(PluginQueryInterface))]
  public PluginQueryInterface QueryInterface { get; set; }

  public MyPlugin() {}
}

or this

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

The 2nd implementation is pretty clearly a circular reference, because the plugins requires that the PluginQueryInterface be created before the plugin is created, but the PluginQueryInterface is the Model, which must import the PluginManager, which in turn needs all of the PluginInterfaces created... and I do get a MEF circular dependency error when I launch.

The 1st implementation doesn't seem like a circular reference to me. If the PluginQueryInterface is a property, then I thought it wouldn't be resolved until it was used. And it isn't used by the constructor at all. So why wouldn't the PluginManager merrily create all of my MyPlugins? I get the same MEF error in both cases.

I have tried to solve this problem by then making the PluginManager implement the PluginQueryInterface, because a) it makes sense anyway and b) it's a known way of dealing with circular dependencies -- make the two interdependent classes instead depend on a third class. Now the problem is that I get a different MEF error! This is what it says:

GetExportedValue cannot be called before prerequisite import 'Company.App.PluginManager..ctor(Parameter="database_filepath", ContractName="PluginManager.filename")' has been set.

WTF? I set breakpoints in my code, and my exported value PluginManager.filename has been set before calling GetExportedValue.

I am totally stumped. Any observations or suggestions would be greatly appreciated right now. I've been banging my head against the MEF-clad wall for hours trying to debug this problem.

(updated)

I didn't think about this earlier, but it could have been differences between plugins, so I deleted one of the two plugins, and now my application loads without MEF errors. I added it back, and it failed again. Then I deleted the other plugin, and it worked. So it looks like this is some other MEF error. It's almost as if it doesn't want me to load more than one plugin with a specific interface... but I am using ImportMany, and wouldn't that have manifested itself as a CardinalityException of some kind?

UPDATE

I don't understand this part of MEF, and hopefully someone here can explain what it's all about. After stepping into the code for some time, I found that my error stemmed from MEF removing import definitions after finding the value!

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

I've never had this problem before, and frankly I'm having a hard time understanding what I'm doing now with my imports and exports that has made this problem surface. I assume that I'm doing something that the MEF designers hadn't intended anyone to do. I could blindly comment out the this._importValues.Remove(definition);, but that couldn't possibly be right. My guess is that this will boil down to the MEF attributes I've used, but since the plugin that imports this value has a creation policy of CreationPolicy.Shared, why would I have a problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花开半夏魅人心 2024-09-26 01:28:51

此外,这可能是线程问题。您应该尝试将容器构建为 ThreadSafe:

Also, this might possibly be a threading issue. You should try to construct the container as ThreadSafe:

http://blogs.microsoft.co.il/blogs/zuker/archive/2011/01/02/mef-thread-safety-and-getexportedvalue.aspx

不…忘初心 2024-09-26 01:28:50

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

Well, I've got a possible solution. I don't have any experience with using this, but using Lazy instantiation seems to help. At least I can move forward without having to change MEF code that I don't completely understand.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文