2011-08-23 15 views
9

例外を記録した後、非同期ブロックの実行中に発生した例外を再評価する必要がありました。F#で非同期ワークフローでreraiseを使用するには?

コンパイラは、私がハンドラ内から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

ラフ!私はこれは不可能だと思う。なぜなら、reraiseはスタックの上から例外を取る特殊なIL命令に対応するが、非同期式が連続のチェーンにコンパイルされる方法では、私はセマンティクスが成り立たないと思う!

同じ理由

、次のようにコンパイルされません、次のいずれか

私は実際 with体の外に例外を処理する必要がある、と reraiseをエミュレートしたいと思いますこのような状況では、
try 
    (null:string).ToString() 
with e -> 
    (fun() -> 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'クラスを使うことができます。また、原点と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

+1

これは具体的には、別のスレッドからのリレイズを呼び出せないため、別のスタックフレームから呼び出すことはできません。 –

関連する問題