2011-08-23 45 views
9

我需要在记录异常之后重新执行异步块时发生的异常。如何在F#中的异步工作流程中使用重新展示?

当我执行以下操作时,编译器认为我没有从处理程序中调用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) 
} 

回答

11

粗糙!我认为这是不可能的,因为重新生成对应于一个特殊的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

+1

这个答案也许是过时的除外。在.net 4.5中,你可以使用'ExceptionDispatchInfo'类,它执行此操作,并捕获Watson桶信息,如原点集和IL偏移量。 http://msdn.microsoft.com/en-us/library/system.runtime.exceptionservices.exceptiondispatchinfo(v=vs.110).aspx –

+0

@DaxFohl可以使用'ExceptionDispatchInfo'提供更新的答案? –

3

我遇到了类似的问题,在不同的背景下,但归结到这一点。

异常不能抛出到不同的线程 - 调用reraise()将需要异常处理程序在代码中的原始异步块“上面”某种意义上运行。

let runAsync context = async {return()} 
let isCriticalException e = true 
let logCriticalException e =() 
let logException e =() 
let executeAsync context = 
    async { 
      do! runAsync context 
      return None 
} 

let run = 
    match executeAsync 5 |> Async.Catch |> Async.RunSynchronously with 
    |Choice1Of2(t) -> 
     printfn "%A" t 
     None 
    |Choice2Of2(exn) -> 
      match exn with 
      | e when isCriticalException(e) -> 
       logCriticalException e 
       raise (new System.Exception("See inner exception",e)) //stack trace will be lost at this point if the exn is not wrapped 
      | e -> 
       logException e 
       Some(e) 

注意,我们仍然无法使用再加注,因为我们现在要求在不同的线程,所以我们总结里面一个又一个

+1

它更具体,然后不能从另一个线程调用reraise,它不能从不同的栈帧调用。 –

相关问题