2011-08-23 2 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

거친! 재사용은 스택 상단에서 예외를 가져 오는 특수 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 오프셋의 어셈블리와 같은 Watson 버킷 정보를 캡처합니다. http://msdn.microsoft.com/en-us/library/system.runtime.exceptionservices.exceptiondispatchinfo(v=vs.110).aspx –

+0

@DaxFohl은 'ExceptionDispatchInfo'를 사용하여 업데이트 된 답변을 제공해 주시겠습니까? –

3

다른 문제에서 유사한 문제가 발생했지만 그 문제가 있습니다.

다른 스레드로 예외를 throw 할 수 없습니다. 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

더 구체적으로 다른 스레드에서 재현을 호출 할 수 없으면 다른 스택 프레임에서 호출 할 수 없습니다. –

관련 문제