我之前从未见过的新MEF错误 - “导出不能分配给类型......”

时间:2010-12-22 23:49:07

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

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


当海报确保他的装配参考是一致的时候,

This one就解决了。我现在没有这个问题,因为我目前正在我的解决方案中引用另一个项目。

当海报被指示使用ImportMany时,

This one已解决,但我已经在使用它(我也认为正确)尝试加载多个插件

当海报意识到平台目标不匹配时,

This one得以解决。我已经完成了我的项目,以确保一切都以x86为目标。


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

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

我的“slave”插件程序集定义了一个同样实现和导出IConnectionSharer的类。

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

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

尼古拉斯让我出示我的Export,所以他们在这里。

“Master”插件:

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

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

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

但在部件组合期间,我收到错误导出'公司。 MasterPlugin (ContractName =“IConnectionSharer”)'不能分配给'IConnectionSharer'类型。 < / p>

错误消息本身看起来很清楚 - 就好像MEF认为我的主插件不会继承IConnectionSharer ......但确实如此!谁能建议进一步的调试策略?我将开始单步执行MEF源的痛苦过程。

更新

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

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

HUH?显然AllSharedConnections IEnumerable<T>,为什么MEF会抱怨?

4 个答案:

答案 0 :(得分:3)

你很可能在输出文件夹中有一个旧版本的程序集,例如:重命名后。通常这不是问题,因为没有其他程序集引用这个旧程序集。但是,使用MEF,只需存在于应用程序文件夹中即可为组合拾取组件。

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

修改:因为清理输出文件夹时问题不会消失,我的下一个猜测就是你有两个声明{{1}或者您正在将相同的代码编译为两个不同的程序集。这将导致两个IConnectionSharer接口具有相同的名称但具有不同的标识。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

我的目的是允许“slave”插件从“master”连接“借用”连接,并通过我调用IConnectionSharer的接口执行此操作。好消息是我已经确认它有效 - 我只需要弄清楚我的MEF错误。

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

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

alt text

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

问题是DongleConnection和DongleConnection2都与IFeature相关联,IConnectionSharer在每个程序集中定义。当我添加{{1}}时,该接口也在每个程序集中。我的应用程序加载两个主插件,因此当从属插件加载时,从属插件请求IFeature的那一点存在歧义。

至少这是我对这个问题的理解。如果您认为我通过分析误导任何人,我将不胜感激。在搞清楚之后,事实证明Wim有确切的答案(我只是在发布这个答案后看到了他的编辑,所以我不得不改变我给出答案的人!)。我只是没有想到重复合同集合的来源,因为DongleConnection和DongleConnection2类的神奇细节早已被遗忘。 :)

我现在可以完成所有工作,但我的解决方案不是最优的,因此我将不得不针对其他可能的解决方案发布不同的问题。

答案 3 :(得分:0)

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

这些可能会有所帮助:

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