2011-08-23 4 views
9

Tôi cần phải khắc phục một ngoại lệ xảy ra khi thực hiện một khối không đồng bộ, sau khi ghi ngoại lệ.Làm thế nào để sử dụng reraise trong quy trình công việc không đồng bộ trong F #?

Khi tôi làm như sau trình biên dịch nghĩ rằng tôi không gọi hàm reraise từ bên trong trình xử lý. Tôi đang làm gì sai?

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

Trả lời

11

Thô! Tôi nghĩ rằng điều này là không thể, bởi vì reraise tương ứng với một lệnh IL đặc biệt mà lấy ngoại lệ từ phía trên cùng của ngăn xếp, nhưng cách biểu thức async được biên dịch thành chuỗi liên tục, tôi không nghĩ rằng ngữ nghĩa được giữ lại!

Đối với lý do tương tự, sau đây sẽ không biên dịch hoặc là:

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

Trong những tình huống này, nơi mà tôi cần để xử lý các ngoại lệ bên ngoài của cơ thể with thực tế, và muốn bắt chước reraise (mà được, giữ gìn stack trace của ngoại lệ), tôi sử dụng giải pháp this, vì vậy tất cả cùng mã của bạn sẽ trông như thế:

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

cập nhật: NET 4. 5 đã giới thiệu ExceptionDispatchInfo có thể cho phép triển khai sạch hơn reraisePreserveStackTrace ở trên.

+1

Câu trả lời này có lẽ là lỗi thời. Trong .net 4.5, bạn có thể sử dụng lớp 'ExceptionDispatchInfo', nó thực hiện điều này và cũng thu thập thông tin xô Watson như lắp ráp nguồn gốc và bù đắp IL. http://msdn.microsoft.com/en-us/library/system.runtime.exceptionservices.exceptiondispatchinfo(v=vs.110).aspx –

+0

@DaxFohl có thể vui lòng cung cấp câu trả lời cập nhật bằng cách sử dụng 'ExceptionDispatchInfo'? –

3

Tôi đã gặp phải vấn đề tương tự, trong một ngữ cảnh khác, nhưng nó lại rơi vào vấn đề này.

Ngoại lệ không thể được ném lên một luồng khác - gọi reraise() sẽ yêu cầu trình xử lý ngoại lệ chạy theo một nghĩa nào đó 'ở trên' khối không đồng bộ gốc trong mã.

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) 

Lưu ý, chúng tôi vẫn không thể sử dụng reraise, như chúng ta đang kêu gọi một chủ đề khác nhau, vì vậy chúng tôi quấn ngoại lệ bên trong một một

+1

Nó cụ thể hơn sau đó không thể gọi reraise từ một chủ đề khác, nó không thể được gọi từ một stackframe khác nhau. –