【问题标题】:A new MEF error I've not seen before -- "The export is not assignable to type..."我以前从未见过的新 MEF 错误——“导出无法分配给类型...”
【发布时间】:2010-12-22 23:49:07
【问题描述】:

今天收到这个错误让我感到非常惊讶,因为这是我以前从未遇到过的错误。代码中的一切看起来都不错,所以我做了一些搜索。之前的问题及其各自的答案没有帮助。


This one 在张贴者确保他的程序集引用一致时得到解决。我现在没有这个问题,因为我目前正在我的解决方案中引用另一个项目。

This one 在发帖人被指示使用 ImportMany 时已解决,但我已经在使用它(我认为也是正确的)尝试加载多个插件

This one 在发帖人意识到平台目标不匹配时解决。我已经完成了我的项目以确保所有内容都针对 x86。


这就是我想要做的。我有一个拥有与设备连接的插件。我可能还需要能够与另一个插件共享该连接。我认为最简洁的方法是创建一个接口,允许从属插件请求自己与设备的连接。我们就叫它IConnectionSharer吧。如果从属插件确实不需要需要借用这个连接并且有自己的,那么它应该使用自己的IConnectionSharer实现来连接到设备。

我的“主”插件(拥有设备连接的插件)实现了IConnectionSharer。它还通过 ExportAttribute 导出它。

我的“从属”插件程序集定义了一个类,该类还实现和导出IConnectionSharer

当应用程序加载时,我的 slave 插件通过 MEF 枚举所有 IConnectionSharers 并将它们存储在 IEnumerable<IConnectionSharer> 中。这样做是这样的:

[ImportMany]
public IEnumerable<IConnectionSharer> AllSharedConnections { get; set; }

尼古拉斯让我出示我的Exports,所以他们来了。

“大师”插件:

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(Interface1))]
[Export(typeof(Interface2))]
[Export(typeof(Interface3))]
[Export(typeof(IConnectionSharer))]
public partial class MasterPlugin : Interface1, Interface2, Interface3, IConnectionSharer
{
    ...
}

“从”插件:

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(Interface1))]
public class SlavePlugin : Interface1
{
    private Model _model { get; set; }
    private ViewModel _viewmodel { get; set; }

    [ImportingConstructor]
    public SlavePlugin( [Import] Model model) 
    {
        _model = model;
        _viewmodel = new ViewModel( model);
    }
}

型号:

[Export(typeof(Model))]
public class Model
{
    [ImportMany(typeof(IConnectionSharer))]
    private IEnumerable<IConnectionSharer> AllSharedConnections { get; set; }
    ...
}

“从”插件内部的 IConnectionSharer 实现:

[Export(typeof(IConnectionSharer))]
public class PrivateConnection : IConnectionSharer
{
    ...
}

但在部件组合过程中,我收到错误导出“Company.MasterPlugin (ContractName="IConnectionSharer")' is notassignable to type 'IConnectionSharer'。

错误消息本身似乎很清楚——就好像 MEF 认为我的主插件没有从 IConnectionSharer 继承......但确实如此!谁能建议进一步的调试策略?我将开始单步调试 MEF 源的痛苦过程。

更新

这是一个有趣的线索——如果在清除我的输出文件夹并重建解决方案之后,我删除“主”插件(这样“从”插件将使用它自己的 IConnectionSharer对象),我的应用程序加载得很好,我的从属插件也按预期运行。如果我将主插件放回 plugins 文件夹,我会再次遇到 MEF 组合问题。

我还想尝试使用 Lazy 实例化来查看是否有任何效果。结果有点令人吃惊。 MEF 抱怨此错误:无法填充集合“AllSharedConnections”,因为它没有实现 ICollection 或者是只读的。如果集合不是 IEnumerable&lt;T&gt; 或 T[],它必须实现 ICollection 并且可以预先初始化或使用默认构造函数写入。

哈?显然 AllSharedConnections IEnumerable&lt;T&gt;,那么为什么 MEF 会抱怨呢?

【问题讨论】:

  • 您使用的是哪种目录?什么样的应用程序?您是否尝试过从主机引用插件程序集?你的出口是什么样的? - hth,尼克
  • 我使用的是AggregateCatalog,因为我需要查看几个文件夹(两个DirectoryCatalog)。有一次我还必须添加一个AssemblyCatalog,因为没有它,由于某种原因我无法导入我的 ViewModel(不确定为什么 DirectoryCatalog 不会覆盖它,因为可执行文件在目录中)。我将使用导出更新上面的代码。我没有尝试硬引用插件程序集,因为我总是从 MEF 开始并且只想让它工作。不过我可以试试。谢谢!

标签: c# wpf visual-studio-2010 .net-4.0 mef


【解决方案1】:

您的输出文件夹中很可能有旧版本的程序集,例如重命名后。通常这不是问题,因为没有其他程序集引用这个旧程序集。但是,使用 MEF,只需将程序集放在应用程序文件夹中,就可以为组合选取程序集。

清理您的输出文件夹,重建并查看问题是否消失。

编辑:由于清理输出文件夹时问题并没有消失,我的下一个猜测是您有 两个 IConnectionSharer 声明,或者您正在将相同的代码编译成两个不同的程序集。这将导致两个IConnectionSharer 接口名称相同但标识不同。

【讨论】:

  • 我真的没有这个问题,因为我没有重命名任何东西,我正在构建我的整个解决方案,它只是重新创建所有插件。但显然我错过了一些东西,因为在删除所有内容后,我得到了一个不同的 MEF 错误。 :) 我会继续挖掘。谢谢你的建议,维姆。
  • @Dave:不必重命名。如果您从解决方案中删除项目,也会发生同样的情况,因为输出文件夹中的程序集仍然存在。
  • @Wim 是的,这是真的。我定期忘记清除我的输出文件夹。 :) 无论如何,我做到了,这发现了一个与 MEF 相关的不同问题,但我只是修复了它,所以现在我回到了我原来的 MEF 问题。又是同样的错误。
  • @Wim 我刚刚看到了你的编辑!!!我希望我早点看到它——我终于明白了。事实证明你是完全正确的。我已经在我自己的答案中发布了这些信息,这证实了你的怀疑。伙计,你很好。 :)
  • @Wim 代码共享问题是一个单独的问题,所以我将开始一个新问题并在此处发布链接。我很感激您能提供有关该特定问题的任何 cmets / 答案。
【解决方案2】:

出于好奇,请检查包含 IConnectionSharer 类型的程序集是否在调试器下加载了两次。程序集可能会在流程中的多个上下文中加载,在这种情况下,如果它们从加载的程序集的不同版本引用,则应该相同的两种类型看起来会有所不同。

【讨论】:

  • 我只是在 ComposeParts 上设置了一个断点,然后打开了“模块”窗口——没有重复。
【解决方案3】:

我知道是什么导致了我的问题,但我不确定解决问题的最佳方法。我现在将发布解决方案,但随后会发布一个新问题,因为它与 MEF 没有任何关系。

如前所述,我有一个通过加密狗连接到 PC 的设备。我希望能够与另一台设备共享该加密狗,但加密狗的第 3 方软件不允许同时连接到它。

我的意图是允许“从属”插件从“主”连接“借用”连接,并通过我称为IConnectionSharer 的接口来执行此操作。好消息是我已经确认它有效——我只需要找出我的 MEF 错误。

通过使用 C# DLL(它使用 C# 包装类通过 PInvoke 调用 C DLL)调用它们的 C DLL 来实现与加密狗的接口。然而,实际上我有两个这样的加密狗,但是第 3 方库不允许在计算机上使用两个。我能够通过将我的 C# DLL 包装器的另一个副本创建为一个完全独立的程序集来欺骗它工作。该程序集仅具有指向我的其他 C# DLL 中的 .cs 文件的链接,但具有其 C# 包装类的不同副本。我修改了这个副本以调用他们的 C DLL 的不同副本,以强制它加载到进程内存空间中的不同地址。到目前为止,它对我们来说非常有效,经过多年的使用,没有任何沟通问题。

现在可能需要一张UML图来指出问题的根源:

如您所见,一个“主”插件将通过 DongleConnection 连接到一个加密狗,而另一个“主”插件将通过 DongleConnection2 连接到另一个加密狗。这两个类都在相同的命名空间中。 “从”插件将需要通过一个加密狗或另一个通过 IConnectionSharer 进行通信。它通过调用RequestFeature() 来访问“功能”。

问题在于 DongleConnection 和 DongleConnection2 都与IFeature 相关联,IFeature 是在它们各自的程序集中定义的。当我添加IConnectionSharer 时,该接口也在这些程序集中。我的应用程序加载了两个主插件,因此当从属插件加载时,从属插件请求 IFeature 时存在歧义。

至少这是我对问题的理解。如果您认为我的分析误导了任何人,我将不胜感激。弄清楚这一点后,事实证明 Wim 有确切的答案(我刚刚在发布此答案后看到了他的编辑,所以我不得不更改我授予答案的人!)。我只是没有考虑重复合约组件的来源,因为 DongleConnection 和 DongleConnection2 类的神奇细节早已被遗忘。 :)

我现在可以完成这一切,但我的解决方案不是最佳的,所以我必须就其他可能的解决方案发布一个不同的问题。

【讨论】:

    【解决方案4】:

    您是否有可能有两个不同版本的合同程序集,并且每个版本都在加载?一个可能来自可执行路径,另一个来自插件路径。

    这些可能会有所帮助:

    http://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx

    http://msdn.microsoft.com/en-us/library/dd153782.aspx

    【讨论】:

    • 我的插件文件夹中只有该程序集,并且它不存在于应用程序文件夹中...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多