2011-08-23 11 views
9

Avevo bisogno di reraise un'eccezione che si verifica durante l'esecuzione di un blocco asincrono, dopo aver registrato l'eccezione.Come utilizzare il reraise nei flussi di lavoro asincroni in F #?

Quando faccio quanto segue il compilatore pensa che non sto chiamando la funzione di reraise dall'interno del gestore. Che cosa sto facendo di sbagliato?

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) 
} 

risposta

11

Rough! Penso che questo sia impossibile, perché il reraise corrisponde ad una speciale istruzione IL che afferra l'eccezione dalla cima della pila, ma il modo in cui le espressioni asincrone sono compilate in una catena di continuazioni, non credo che la semantica regga!

Per lo stesso motivo, il seguente non verrà compilato uno:

try 
    (null:string).ToString() 
with e -> 
    (fun() -> reraise())() 

In queste situazioni, in cui ho bisogno per gestire l'eccezione di fuori del with corpo reale, e vorrebbe emulare reraise (che è, conservare la traccia dello stack dell'eccezione), io uso this soluzione, quindi tutti insieme il codice sarà simile:

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) 
} 

Aggiornamento: .NET 4. 5 ha introdotto ExceptionDispatchInfo che potrebbe consentire un'implementazione più pulita di reraisePreserveStackTrace precedente.

+1

Questa risposta è forse obsoleto. In .net 4.5 è possibile utilizzare la classe 'ExceptionDispatchInfo', che esegue questa operazione e acquisisce anche informazioni bucket Watson come assembly of origin e IL offset. http://msdn.microsoft.com/en-us/library/system.runtime.exceptionservices.exceptiondispatchinfo(v=vs.110).aspx –

+0

@DaxFohl potrebbe fornire una risposta aggiornata usando 'ExceptionDispatchInfo'? –

3

Mi sono imbattuto in un problema simile, in un contesto diverso, ma si riduce a questo.

Le eccezioni non possono essere generate su un thread diverso: chiamare reraise() richiederebbe un gestore di eccezioni che esegue in un certo senso "sopra" il blocco asincrono originale nel codice.

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) 

nota, ancora non è possibile utilizzare reraise, come siamo ora chiamata su un thread diverso, quindi abbiamo avvolgere l'eccezione all'interno di un altro

+1

È più specifico che non essere in grado di chiamare il reraise da un thread diverso, non può essere chiamato da un altro stackframe. –

Problemi correlati