在记录异常之后,我需要重新引发在执行异步块时发生的异常。
当我执行以下操作时,编译器认为我没有从处理程序中调用reraise函数。我究竟做错了什么?
let executeAsync context = async {
traceContext.Properties.Add("CorrelationId", context.CorrelationId)
try
do! runAsync context
return None
with
| e when isCriticalException(e) ->
logCriticalException e
reraise()
| e ->
logException e
return Some(e)
}
最佳答案
粗糙的!我认为这是不可能的,因为重新引发对应于一条特殊的IL指令,该指令从堆栈的顶部捕获异常,但是异步表达式被编译为一系列连续的方式,我认为语义不成立!
出于相同的原因,以下内容也不会编译:
try
(null:string).ToString()
with e ->
(fun () -> reraise())()
在这些情况下,我需要在实际的
with
主体之外处理异常,并且想要模拟reraise
(即,保留异常的堆栈跟踪),我使用this解决方案,因此所有代码都看起来像:let inline reraisePreserveStackTrace (e:Exception) =
let remoteStackTraceString = typeof<exn>.GetField("_remoteStackTraceString", BindingFlags.Instance ||| BindingFlags.NonPublic);
remoteStackTraceString.SetValue(e, e.StackTrace + Environment.NewLine);
raise e
let executeAsync context = async {
traceContext.Properties.Add("CorrelationId", context.CorrelationId)
try
do! runAsync context
return None
with
| e when isCriticalException(e) ->
logCriticalException e
reraisePreserveStackTrace e
| e ->
logException e
return Some(e)
}
更新: .NET 4.5引入了ExceptionDispatchInfo,它可以使上面的
reraisePreserveStackTrace
的实现更干净。