Как использовать ререйз в асинхронных рабочих процессах в F #? - PullRequest
11 голосов
/ 24 августа 2011

Мне нужно было вызвать исключение, которое возникает при выполнении асинхронного блока, после регистрации исключения.

Когда я делаю следующее, компилятор думает, что я не вызываю функцию 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)
}

Ответы [ 2 ]

13 голосов
/ 24 августа 2011

Грубые!Я думаю, что это невозможно, потому что reraise соответствует специальной инструкции IL, которая захватывает исключение из верхней части стека, но то, как асинхронные выражения компилируются в цепочку продолжений, я не думаю, что семантика верна!

По той же причине следующее не будет компилироваться:

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

В этих ситуациях, когда мне нужно обработать исключение за пределами фактического with тела, и я хотел бы эмулироватьreraise (то есть сохранить трассировку стека исключения), я использую это решение, поэтому весь ваш код будет выглядеть так:

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 выше.

3 голосов
/ 24 августа 2011

Я столкнулся с подобной проблемой, в другом контексте, но это сводится к этому.

Исключения не могут быть выброшены в другой поток - вызов 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)

Обратите внимание, что мы все еще не можем использовать ререйз, так как теперь мы вызываем другой поток, поэтому мы заключаем исключение в другой

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...