使用 MEF 忽略构造函数异常而不 Visual Studio 在异常处中断

Ignore constructor exceptions with MEF without Visual Studio breaking at the exception

我想创建一些现有的代码模块 (IMyDesiredType) 以使用 MEF 加载。这些模块大多有一些构造函数参数,我想用 MEF (ImportingConstructor) 提供这些参数。到目前为止,一切正常。

现在出现问题是因为有时依赖项在宿主应用程序中不可用(它们为空)。这些模块将按照惯例抛出一个 ArgumentNullException,我不想改变它。但是我希望 MEF 忽略这些对象(不将它们包含在对象图中)。

 [Export(typeof(IMyDesiredType))]
 class MyModule : IMyDesiredType{
    [ImportingConstructor]
    public MyModule(object aNecessaryDependency){
       if(aNecessaryDependency==null) throw new ArgumentNullException(nameof(aNecessaryDependency))
     }
 }

为了获得这个,我让 MEF 创建 Lazy<IMyDesiredType> 个实例并一个一个地初始化它们。

foreach(var myLazy in collectionOfMefExports){
  try{
      myLazy.Value // do something with the value, so the object gets composed
   }catch(CompositionException){
      // Here I get the ArgumentNullException wrapped in a CompositionException
      // and also can work around it. However because of the exception handling
      // is on the first hand in MEF, VS will break always in the throwing 
      // constructor of the module
      continue; // Go to the next module after logging etc.
   }
} 

这里的问题是,我必须从模块的构造函数中捕获 CompositionException 而不是直接捕获 Exception(主要是 ArgumentNullException)。因此,Visual-Studio 在每个模块上中断,因为未从用户代码中捕获异常。解决这个问题的明显方法是告诉 visual studio 不要在 ArgumentNullException 类型上中断,但这对我来说感觉很“hackish”。在任何其他地方,我希望 VS 在 ArgumentNullExceptions.

上中断

是否有另一种模式可以使 MEF 不向图中添加组件,其中声明了依赖项 ([Export]),但它的值为 null,或者是否有 MEF 的方法-class 我可以在派生的 class 中覆盖并在正手上捕获构造函数异常?

请发表评论,如果问题不清楚,我不是以英语为母语的人,因此问题的措辞可能有点令人困惑。

听起来好像只有您知道哪些组件重要哪些不重要。 您可能想像现在一样将组件初始化包装在 try catch 中,并通过配置查找确定此特定异常在代码中是否有问题。

基本上您需要自己构建规则,但无论哪种方式,失败的组件加载都是失败的,因此我认为抛出异常的组件无法正确添加到图表中。

普遍接受的方法似乎是删除故障组件然后重新组合图...

How do I get MEF to recompose when I change a part?

...

这可能会帮助您诊断下一步如何/做什么..

https://blogs.msdn.microsoft.com/dsplaisted/2010/07/13/how-to-debug-and-diagnose-mef-failures/

我可能走错了方向,但是默认实现未处理案例的抽象工厂有帮助吗?

我将它用于我对 unity DI 的依赖...毫无疑问是个有趣的问题!

<Export(GetType(IComponent))>
Public Class DependencyResolver
    Implements IComponent
    Public Sub SetUp(registerComponent As IRegisterComponent) Implements IComponent.SetUp
        'General
        registerComponent.RegisterType(Of IDataContextAsync, dbEcommEntities)()  

        'DomainLogic
        registerComponent.RegisterType(Of IUserDomainLogic, MfrUserDomainLogic)()

        'Services
        registerComponent.RegisterType(Of ICompanyService, CompanyService)()

    End Sub

End Class

注入构造函数 -- 我将我的 MEF 导出到另一个 class 库中,它有自己的依赖解析器。然后在每个 Web 应用程序中为新应用程序注册组件。然后,我可以通过注入我的 mvc 控制器的构造函数来扩展我从 MEF 导出依赖项解析器收到的每个部分。 在组件加载器中,我然后用 dll 加载容器,并且还能够通过在事后添加 unity.config 来扩展依赖关系。

Private Shared Sub RegisterDependencies(container As IUnityContainer)
        'load services 
        ComponentLoader.LoadContainer(container, ".\bin", "Service.dll")

        'load config
        'container.LoadConfiguration()
    End Sub

自 MEF 预览版 6 以来,MEF 已经将此作为一项功能包含在内,它被称为 稳定组合。即使未提供依赖项,MEF 也会安全启动。

解决您问题的一个简单方法是在访问该值之前检查该值是否实际创建。

if(myLazy.IsValueCreated)
    myLazy.Value // do something with the value, so the object gets composed

您可以阅读更多相关信息 here

Note: If you have optional dependencies and system can work without them then do not put Argument null check in constructor. It will make them Required dependency during the composition.

正如您已经提到的,您不想更改参数为 null 时抛出异常的约定。好吧,如果您知道系统可以在没有这些依赖项的情况下工作,那么您就可以避免对此类依赖项进行检查。

不幸的是,对您所请求的支持在某种程度上是有限的。

Visual Studio 允许根据异常类型配置调试器是否应该中断。当您尝试 show/hide 基于执行上下文的相同类型的异常时,这并没有真正帮助。您仍然可以派生自己的异常类型并在导入构造函数中使用它。这将允许您按异常类型配置中断,但不会在 MEF 组合和其他代码之间产生差异。

此外,调试器可以将方法标记为忽略。请参阅此相关答案 Don't stop debugger at THAT exception when it's thrown and caught

无视 [DebuggerStepThrough] 因为据报道它不可靠,选项一是 [DebuggerHidden]。我想添加另一个候选人:[DebuggerNonUserCode]。这与 VS 选项 "Enable just my code" 一起播放(参见 http://blog.functionalfun.net/2008/05/debuggernonusercode-suppressing.html)。

因此,虽然 [DebuggerHidden] 永远不会在构造函数中抛出异常时中断,而是会在下一个周围的用户代码中报告它,[DebuggerNonUserCode] 允许您忽略或中断构造函数取决于您的 VS 调试设置。只要设置了 "Enable just my code",两个属性的行为应该相同。

假设 MEF 初始化完全在调试器隐藏代码中处理,对于非 MEF 构造函数调用,调试器将在它首次到达未标记为隐藏的周围函数时中断。