2011-08-23 88 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,它不能從不同的棧幀調用。 –

相關問題