c# – 如何重新抛出TargetInvocationException的内部异常,而不会丢失堆栈跟踪

我有很多方法调用使用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(); 
}
最佳答案
如果你只是想重新抛出一个保留它的堆栈跟踪的内部异常,你可以用这样一个方法:

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

更新:官方API已添加到.NET 4.5:ExceptionDispatchInfo

转载注明原文:c# – 如何重新抛出TargetInvocationException的内部异常,而不会丢失堆栈跟踪 - 代码日志