我有许多使用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();
}
答案 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