MEF框架学习之旅(十一)调试

此章节来自msdn。

一、一般调试方法

在 Managed Extensibility Framework (MEF) 中调试问题可能非常困难,因为潜在问题与标准应用程序中的潜在问题不同。 本主题提供了特定于 MEF 的问题诊断技巧,并且提供了这些问题的一些可能原因。

发现 MEF 问题

解决 MEF 问题的第一步是在应用程序的 MEF 部分中定位问题。 下表列出了特定于 MEF 的问题。

问题

可能的原因

组合期间引发 ImportCardinalityMismatchException 异常。

由于部件缺失或被拒绝,无法使用匹配的导出填充导入。

- 或 -

预期单个导出的导入发现多个匹配项。

具有 ImportManyAttribute 特性的集合缺少预期内容。

预期部件缺失或被拒绝。

DefaultValueAttribute 特性设置为 true 的导入意外未填充。

预期的匹配项缺失或被拒绝。

处理异常和 DisableSilentRejection


MEF 旨在可靠地适应各种配置。 默认情况下,它忽略具有缺少必需的依赖项的部分。 但是,当您调试应用程序时,这种可靠性会使查找问题原因更困难。 您可以采用这些步骤更轻松地跟踪问题:

引发异常时,请配置 Visual Studio 调试器中断。

配置您的 CompositionContainer 对象禁用拒绝。

通常,如果应用程序引发异常,则 Visual Studio 调试器将中断执行仅当该异常不随后处理。 但是,您可能会发现,当引发异常时,立即停止并调试您的 MEF 应用程序会很有帮助,因为您的应用程序会以某些方式处理异常,而不会将其体现在调试器中。 若要在引发异常时配置 Visual Studio 调试器,在菜单栏上,选择调试异常。 确保选择了所有异常的引发框,您想要将调试器设置为遇到这些异常时中断。 (有关 MEF,这些是在 System.ComponentModel.Composition 下的异常。这些异常此时将显示在调试器中,即使您的应用程序处理了这些异常。

默认情况下,MEF 使用叫做拒绝的处理确定将用于实例化调用哪些部件具有相关性。 组合引擎检查新部件的添加是否将导致组合失败。 如果它们可能会导致失败,则从容器中隐藏那些部件,但不引发异常。 在面对部署配置时此行为会使应用程序更稳定,但可能使调试更难。 若要每当部件被拒绝时关闭拒绝,并确保 MEF 引发异常,将 DisableSilentRejection 值设置传递到 CompositionContainer 或 ExportProvider 对象。 以下代码对此进行了说明:

 

代码段

var catalog = new DirectoryCatalog("Extensions");
var container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection);


MEF 失败通常是级联的,即组合一个部件失败会导致组合第二个部件失败以此类推,直到观测到的失败点在异常的长列表中丢失。 为帮助诊断此属性的问题,CompositionException 异常提供 RootCauses 属性。 您可以检查在调试器中的此属性以查找是组合中每个错误的根本原因的异常。

跟踪


从开始 .NET Framework 4.5,MEF 支持跟踪。 运行时,以 Visual Studio 的调试模式,组合故障和异常将写入“IntelliTrace”窗口。 检查这些结果有助于诊断组合失败。 有关使用 IntelliTrace 的更多信息。

检查可用部件


若要确定哪些部件可用于目录,可以在调试器中检查目录的 Parts 属性,在代码中循环访问该属性。

如果希望看到的部件不存在,则表示未发现或者已拒绝这些部件。

如果可以看到一个部件,但是它与您希望它填充的导入不匹配,则表示出现了某种类型的不匹配。 

导出/导入不匹配


为了使导出与特定导入匹配,必须满足以下所有条件。 如果未出现预期匹配,但是您已确认导出存在于目录中,请仔细检查这些条件。 手动构造协定以匹配特定导出时,这些条件也适用。

属性

匹配的条件

协定名称

必须完全匹配并且区分大小写。 如果协定名称是从类型推断得出的(例如,在没有任何参数的情况下应用 ExportAttribute 特性时),则唯一可能的匹配项是从同一类型推断得出的另一名称。

协定类型

必须完全匹配。 不支持多态匹配。 即使提供了匹配的协定名称,协定类型也必须匹配。

必需的元数据

导入通过其元数据视图的属性要求的所有元数据必须由导出通过 ExportMetadataAttribute 特性或自定义元数据特性提供。 元数据键(元数据视图中属性的名称)和元数据值的类型必须匹配。

创建策略

导出和导入不能指定不同的创建策略。 有关更多信息,请参见 CreationPolicy。

发现问题


如果部件未显示在目录中或者使用组合分析工具 (Mefx) 时未显示,则表示目录未发现该部件。 这里是导致此故障的一些可能原因:

该部件是抽象‏‎‏‏‏‏‎‎类型。 抽象类型不能用作部件。 使类型成为非抽象类型,或者创建非抽象子类型。

ImportingConstructorAttribute 特性缺失。 对于具有多个构造函数或者仅具有接受参数的构造函数的部件,必须指定一个构造函数,以便 MEF 使用 ImportingConstructorAttribute 特性。

该部件具有 PartNotDiscoverableAttribute 特性。 此特性阻止部件被发现。

该部件是开放式泛型类型。 MEF 不支持开放泛型。 请使用类型的封闭式子类,或者导出单个属性。

您使用 DirectoryCatalog 对象时可能出现其他故障:

该部件位于 .exe 文件中。 默认 DirectoryCatalog 仅从 DLL 文件读取。 通过使用相应搜索模式创建,可以使用 DirectoryCatalog 对象从其他文件读取。

部件的程序集具有缺失的引用。 使用的程序集不能从搜索路径(通常从它们自己的目录或者从全局程序集缓存)加载其引用。

该部件的程序集面向其他 CPU 类型。 MEF 不会加载面向错误 CPU 类型的程序集。

 

二、组合分析工具(Mefx)

组合分析工具 (Mefx) 是分析包含 Managed Extensibility Framework (MEF) 部件的库 (.dll) 和应用程序 (.exe) 文件的命令行应用程序。 Mefx 的主要目的是向开发人员提供一种方式,以允许他们无需向应用程序本身添加繁琐的跟踪代码即可诊断 MEF 应用程序中的组合故障。 它还可用于帮助了解第三方提供的库中的部件。 本主题描述如何使用 Mefx 并且提供了语法参考。

本主题包括下列各节。

获取 Mefx


可在 Codeplex 的 Managed Extensibility Framework 上获取 Mefx。 下载和解压缩该工具即可。

基本语法


Mefx 从命令行使用以下格式调用:

mefx [files and directories] [action] [options]

第一组参数指定从中加载部件进行分析的文件和目录。 使用 /file: 开关指定文件,使用 /directory: 开关指定目录。 可以指定多个文件或目录,如下面的示例所示:

mefx /file:MyAddIn.dll /directory:Program\AddIns [action...]

说明

每个 .dll 或 .exe 应仅加载一次。 如果多次加载一个文件,该工具可能会返回不正确的信息。

在文件和目录列表后面,必须指定命令以及该命令的任何选项。

列出可用部件


使用 /parts 操作可列出在加载的文件中声明的所有部件。 结果为简单的部件名称列表。

mefx /file:MyAddIn.dll /parts

MyAddIn.AddIn

MyAddIn.MemberPart

若要获取有关部件的更多信息,请使用 /verbose 选项。 这将输出所有可用部件的更多信息。 若要获取有关单个部件的更多信息,请使用 /type 操作而不是 /parts。

mefx /file:MyAddIn.dll /type:MyAddIn.AddIn /verbose

[Part] MyAddIn.MemberPart from: AssemblyCatalog (Assembly=" MyAddIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

[Export] MyAddIn.MemberPart (ContractName=" MyAddIn.MemberPart")

列出导入和导出


/imports 和 /exports 操作将分别列出所有导入的部件和所有导出的部件。 还可以使用 /importers 或 /exporters 操作列出导入或导出特定类型的部件。

mefx /file:MyAddIn.dll /importers:MyAddin.MemberPart

MyAddin.AddIn

还可以对这些操作应用 /verbose 选项。

查找拒绝的部件


加载可用的部件后,Mefx 使用 MEF 组合引擎组合它们。 不能成功组合的部件称为“拒绝的部件”。 若要列出所有拒绝的部件,请使用 /rejected 操作。

可以将 /verbose 选项与 /rejected 操作结合使用,以输出有关拒绝的部件的详细信息。 在下面的示例中,ClassLibrary1 DLL 包含 AddIn 部件,它导入 MemberPart 和 ChainOne 部件。 ChainOne 导入 ChainTwo,但是 ChainTwo 不存在。 这意味着 ChainOne 被拒绝,这将导致 AddIn 被拒绝。

mefx /file:ClassLibrary1.dll /rejected /verbose

下面显示了前面的命令的完整输出:

[Part] ClassLibrary1.AddIn from: AssemblyCatalog (Assembly="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

  [Export] ClassLibrary1.AddIn (ContractName="ClassLibrary1.AddIn")

  [Import] ClassLibrary1.AddIn.memberPart (ContractName="ClassLibrary1.MemberPart")

    [SatisfiedBy] ClassLibrary1.MemberPart (ContractName="ClassLibrary1.MemberPart") from: ClassLibrary1.MemberPart from: AssemblyCatalog (Assembly="ClassLibrar

y1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

  [Import] ClassLibrary1.AddIn.chain (ContractName="ClassLibrary1.ChainOne")

    [Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '((exportDefinition.ContractName == "ClassLibrary1.ChainOne") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "ClassLibrary1.ChainOne".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.

   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)

   at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)

    [Unsuitable] ClassLibrary1.ChainOne (ContractName="ClassLibrary1.ChainOne")

from: ClassLibrary1.ChainOne from: AssemblyCatalog (Assembly="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

      [Because] PartDefinitionIsRejected, The part providing the export is rejected because of other issues.

 

[Part] ClassLibrary1.ChainOne from: AssemblyCatalog (Assembly="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

  [Primary Rejection]

  [Export] ClassLibrary1.ChainOne (ContractName="ClassLibrary1.ChainOne")

  [Import] ClassLibrary1.ChainOne.chain (ContractName="ClassLibrary1.ChainTwo")

    [Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '((exportDefinition.ContractName == "ClassLibrary1.ChainTwo") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "ClassLibrary1.ChainTwo".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.

   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)

   at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)

需要关注的信息包含在 [Exception] 和 [Unsuitable] 结果中。 [Exception] 结果提供有关部件为什么被拒绝的信息。 [Unsuitable] 结果指明以其他方式匹配的部件为什么无法用于填充导入;在本例中,因为该部件本身因缺失导入被拒绝。

分析主要原因


如果一些部件链接在较长的依赖关系链中,则问题涉及的部件靠近底部时可能会导致整个链被拒绝。 诊断这些问题可能非常困难,因为故障的根本原因并不总是非常明显。 为了帮助诊断问题,可以使用 /causes 操作,它尝试查找所有级联拒绝的根本原因。

对前面的示例使用 /causes 操作仅列出 ChainOne 的信息,它未填充导入是拒绝 AddIn 的根本原因。 /causes 操作可以在正常和 /verbose 选项中使用。

说明

在大多数情况下,Mefx 能够诊断出级联故障的根本原因。 然而,对于部件以编程方式添加到容器的情况、涉及分层容器的情况或者涉及自定义 ExportProvider 实现的情况,Mefx 将无法诊断出原因。 通常,应尽可能避免前面所述的情况,因为故障通常很难诊断。

白名单


/whitelist 选项允许您指定一个文本文件,其中列出应该被拒绝的部件。 然后,将标记意外拒绝。 分析不完整的库或者缺少一些依赖项的子库时,这可能非常有用。 /whitelist 选项可应用于 /rejected 或 /causes 操作。

请考虑一个名为 test.txt 的文件,它包含文本“ClassLibrary1.ChainOne”。 如果对前面的示例运行带有 /whitelist 选项的 /rejected 操作,则将生成以下输出:

mefx /file:ClassLibrary1.dll /rejected /whitelist:test.txt

[Unexpected] ClassLibrary1.AddIn

ClassLibrary1.ChainOne

 

相关阅读:

MEF框架学习之旅(十一)调试

MEF框架学习之旅(十)重组

MEF框架学习之旅(九)部件生命周期及组装通知

MEF框架学习之旅(八)避免发现

MEF框架学习之旅(七)部件的创建规则(策略)

MEF框架学习之旅(六)元数据和元数据视图

MEF框架学习之旅(五)导入导出(下)

MEF框架学习之旅(四)导入导出(上)

MEF框架学习之旅(三)组合

MEF框架学习之旅(二)契约

MEF框架学习之旅(一)概念

posted @ 2013-06-10 10:11  一个人在北京  阅读(2526)  评论(0编辑  收藏  举报