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

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

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

  try { return myDelegate.DynamicInvoke(args); } catch(TargetInvocationException ex) { Func 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(); } 

如果你只想重新抛出保留其堆栈跟踪的内部exception,可以使用如下方法:

 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上已经打开了一条建议。

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

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

你需要知道两者来判断exception的真正原因。 如果DynamicInvoke()调用确实存在问题,故意隐藏TargetInvocationException会让您很难诊断出问题的根源。 避免这样做。

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