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

时间:2017-03-01 10:46:36

标签: c# visual-studio-2015 dependency-injection mef

我想创建一些现有的代码模块(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会在每个模块上中断,因为Exception不会从用户代码中获取。对此明显的解决方案是告诉视觉工作室不要打破ArgumentNullException类型,但这对我来说非常“苛刻”。在任何其他地方,我希望VS打破ArgumentNullException s。

是否有另一种模式,我可以使MEF不向图形中添加组件,其中声明了依赖项([Export]),但它的值为null,或者是否存在MEF类的方法我可以在派生类中重写并在前端捕获构造函数异常吗?

请发表评论,如果问题不明确,我不是母语为英语的人,因此可能会将问题语言化为混乱。

4 个答案:

答案 0 :(得分:1)

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

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

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

忽略[DebuggerStepThrough],因为据报道它不可靠,选项一是[DebuggerHidden]。我想添加另一位候选人:[DebuggerNonUserCode]。这与VS选项一起播放&#34;只启用我的代码&#34; (见http://blog.functionalfun.net/2008/05/debuggernonusercode-suppressing.html)。

因此虽然[DebuggerHidden]永远不会因为抛出它的构造函数中的异常而中断,而是会在下一个周围的用户代码中报告它,[DebuggerNonUserCode]允许您根据构造函数忽略或中断在您的VS调试设置上。只要&#34;只启用我的代码&#34;设置后,两个属性的行为应该相同。

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

答案 1 :(得分:0)

听起来只有你知道哪些组件很重要而哪些组件不重要。 你可能想做一些事情,比如像你现在一样在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/

答案 2 :(得分:0)

我可能正朝着错误的方向前进,但是对于未处理的案例,默认实施的抽象工厂是否有帮助?

我用这个来依靠统一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导出到另一个具有自己的依赖项解析器的类库中。在每个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

答案 3 :(得分:0)

自MEF预览6以来,MEF已将此作为一项功能纳入其中,并将其称为稳定构图。即使没有提供依赖项,MEF也会安全地启动。

解决问题的唯一方法是在访问值之前检查Value是否实际创建。

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

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

  

注意:如果你有可选的依赖项,系统可以在没有它们的情况下工作,那么不要在构造函数中放置Argument null check。它将使它们在合成期间成为必需的依赖项。

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