C# 如何在不丢失堆栈跟踪的情况下重新显示TargetInvocationException的内部异常
我有许多方法正在使用C# 如何在不丢失堆栈跟踪的情况下重新显示TargetInvocationException的内部异常,c#,exception-handling,stack-trace,targetinvocationexception,inner-exception,C#,Exception Handling,Stack Trace,Targetinvocationexception,Inner Exception,我有许多方法正在使用Delegate.DynamicInvoke调用。其中一些方法会进行数据库调用,我希望能够捕获SqlException,而不是捕获targetingException,并搜索其内部以查找实际出了什么问题 我正在使用此方法重新刷新,但它会清除堆栈跟踪: try { return myDelegate.DynamicInvoke(args); } catch(TargetInvocationException ex) { Func<Targe
Delegate.DynamicInvoke
调用。其中一些方法会进行数据库调用,我希望能够捕获SqlException
,而不是捕获targetingException
,并搜索其内部以查找实际出了什么问题
我正在使用此方法重新刷新,但它会清除堆栈跟踪:
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;
}
IIRC不可能完全保留异常,但是可以通过一些反射来保留堆栈跟踪。下面是一篇博客文章,描述了如何做到这一点:您需要记住,为什么.NET会用TargetInvocationException包装异常,而不仅仅是让原始异常通过。这有一个很好的理由,不清楚异常的真正原因来自哪里。是因为DynamicInvoke()调用被阻塞了吗?并非不可能,编译器无法确保传递正确的参数。还是被调用的目标方法本身抛出了所有错误
您需要了解这两者,才能判断异常的真正原因。如果确实是DynamicInvoke()调用的问题,那么故意隐藏TargetInvocationException将使您很难诊断问题的根源。避免这样做。如果您只想重新抛出一个保留堆栈跟踪的内部异常,可以使用如下方法:
public static void Rethrow(this Exception ex)
{
typeof(Exception).GetMethod("PrepForRemoting",
BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(ex, new object[0]);
throw ex;
}
Rx使用该技术(并将其作为扩展方法Exception.prepareforethrow
)公开),异步CTP也通过其自动展开系统使用该技术(无公开的API)
但是,请注意,这种技术在技术上是不受支持的。希望微软将来能为此添加一个官方API。如果您想投票支持Microsoft Connect,请提供有关它的建议
更新:一个官方API已添加到.NET 4.5:。我理解这一推理,但由于显式捕获特定错误的灵活性已不复存在,因此编写消费代码变得更加困难。无论如何,在这种情况下捕获异常非常困难。您对委托目标一无所知,无法猜测它是如何改变程序状态的。因此,在处理异常时无法恢复状态。TIE混淆问题的一种情况是,如果您同时控制引发异常的“内部”代码和要处理异常的外部代码。如果使用将内部代码封装在动态代理中的东西来注入内部代码,那么现在就更难捕获您从内部代码抛出的异常。例如,在ORM中,某些实体类可能会被包装,这最好是周围的代码不需要关心。需要注意的一点是:4.5中的ExceptionDispatchInfo意味着,当Rx根据您的目标是4.0还是4.5重新触发异常时,您会看到不同于Rx的调用堆栈。当目标为4.5而不是4.0时,未处理的ONERROR将使用原始堆栈跟踪重新恢复。更好的解决方案是查看,而不是使用?无论如何,如果没有可用的话,最好使用
PrepForRemoting
ExceptionDispatchInfo。@Kiquenet:当我写这个答案时,ExceptionDispatchInfo
还不存在。当它出来时,我更新了这个答案,提到现在有一个正式的解决方案。找不到可能的副本
public static void Rethrow(this Exception ex)
{
typeof(Exception).GetMethod("PrepForRemoting",
BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(ex, new object[0]);
throw ex;
}