20

我有很多使用Delegate.DynamicInvoke. 其中一些方法会进行数据库调用,我希望能够捕获 aSqlException而不是捕获 theTargetInvocationException并通过其内部查找实际出了什么问题。

我正在使用这种方法重新抛出,但它清除了堆栈跟踪:

 try
 {
      return myDelegate.DynamicInvoke(args);
 }
 catch(TargetInvocationException ex)
 {
     Func<TargetInvocationException, Exception> getInner = null;
     getInner =
        delegate(TargetInvocationException e)
        {
        if (e.InnerException is TargetInvocationException)
            return getInner((TargetInvocationException) e.InnerException);

         return e.InnerException;
        };

     Exception inner = getInner(ex);
     inner.PreserveStackTrace();
     throw inner;
 }

PreserveStackTrace方法是我通过另一篇文章修复的扩展方法(我不知道它实际上做了什么)。但是,这似乎也没有保留跟踪:

public static void PreserveStackTrace(this Exception e)
{
    var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
    var mgr = new ObjectManager(null, ctx);
    var si = new SerializationInfo(e.GetType(), new FormatterConverter());

    e.GetObjectData(si, ctx);
    mgr.RegisterObject(e, 1, si);
    mgr.DoFixups(); 
}
4

3 回答 3

29

如果你只是想重新抛出一个内部异常来保留它的堆栈跟踪,你可以用这样的方法来做到这一点:

public static void Rethrow(this Exception ex)
{
  typeof(Exception).GetMethod("PrepForRemoting",
      BindingFlags.NonPublic | BindingFlags.Instance)
      .Invoke(ex, new object[0]);
  throw ex;
}

这种技术被 Rx 使用(并且被它们作为扩展方法公开Exception.PrepareForRethrow)并且也被 Async CTP 通过其自动解包系统(没有公开公开的 API)使用。

但是请注意,此技术在技术上不受支持。希望微软将来会为此添加官方 API。如果您想投票,则已在 Microsoft Connect 上打开了一项建议。

更新: .NET 4.5 中添加了官方 API ExceptionDispatchInfo:.

于 2010-12-29T19:30:37.717 回答
2

您需要记住为什么 .NET 使用 TargetInvocationException 包装异常,而不是让原始异常通过。这有一个很好的理由,异常的真正原因来自哪里并不明显。是因为 DynamicInvoke() 调用失败了吗?并非不可能,编译器无法确保传递正确的参数。还是被调用的目标方法自己抛出?

您需要知道两者才能判断异常的真正原因。如果这确实是 DynamicInvoke() 调用的问题,那么故意隐藏 TargetInvocationException 将使您很难诊断出问题的根源。避免这样做。

于 2010-12-29T16:24:11.147 回答
0

IIRC 不可能完全保留异常,但是可以通过一些反射保留堆栈跟踪。这是描述如何做到这一点的博客文章:http: //iridescence.no/post/Preserving-Stack-Traces-When-Re-Throwing-Inner-Exceptions.aspx

于 2010-12-29T16:13:44.003 回答