如何在不丢失堆栈跟踪的情况下重新抛出TargetInvocationException的内部异常

时间:2010-12-29 15:55:59

标签: c# exception-handling stack-trace targetinvocationexception inner-exception

我有许多使用Delegate.DynamicInvoke进行调用的方法。其中一些方法进行数据库调用,我希望能够捕获SqlException而不是捕获TargetInvocationException并通过其内部搜索来查找实际出错的地方。

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

 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(); 
}

3 个答案:

答案 0 :(得分: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)使用。

但请注意,此技术在技术上不受支持。希望Microsoft将来会为此添加官方API。如果您想投票,可以在Microsoft Connect上提出建议has been opened

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

答案 1 :(得分:2)

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

您需要知道两者来判断异常的真正原因。故意隐藏TargetInvocationException将为您提供 hard 时间来诊断问题的来源,如果它确实是DynamicInvoke()调用的问题。避免这样做。

答案 2 :(得分:0)

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