Asynchronous 捕获F异步块中的内部异常

Asynchronous 捕获F异步块中的内部异常,asynchronous,exception,f#,c#-to-f#,Asynchronous,Exception,F#,C# To F#,我有一个异步块,在这个块中我从外部C web服务客户端库调用一个异步方法。此方法调用返回数据传输对象,或ApiException类型的自定义异常。最初,我的函数如下所示: 输入味精= |登录成功 |登录错误 |阿皮耶罗 让authUserAsync客户端:客户端模型= 异步的{ do!Async.SwitchToThreadPool 让loginParams=new LoginParamsDtoUsername=some用户名,Password=some密码 尝试 //AuthenticateT

我有一个异步块,在这个块中我从外部C web服务客户端库调用一个异步方法。此方法调用返回数据传输对象,或ApiException类型的自定义异常。最初,我的函数如下所示:

输入味精= |登录成功 |登录错误 |阿皮耶罗 让authUserAsync客户端:客户端模型= 异步的{ do!Async.SwitchToThreadPool 让loginParams=new LoginParamsDtoUsername=some用户名,Password=some密码 尝试 //AuthenticateTasync可能引发自定义ApiException。 let!loggedInAuthor=client.authenticatesync loginparms |>Async.AwaitTask //用loggedInAuthor DTO做一些事情。。。 返回登录成功 具有 |:?ApiException作为ex-> 让msg= 将ex.StatusCode与 |404->登录错误 |错误 返回消息 } 但是我发现这个例外没有被抓住。进一步调查表明,ApiException实际上是内部异常。因此,我将代码更改为:

输入味精= |登录成功 |登录错误 |阿皮耶罗 让authUserAsync客户端:客户端模型= 异步的{ do!Async.SwitchToThreadPool 让loginParams=new LoginParamsDtoUsername=some用户名,Password=some密码 尝试 //AuthenticateTasync可能引发自定义ApiException。 let!loggedInAuthor=client.authenticatesync loginparms |>Async.AwaitTask //用loggedInAuthor DTO做一些事情。。。 返回登录成功 具有 |BaseXn-> 让msg= 将BaseXn.InnerException与匹配 |:?作为e-> 将e.StatusCode与 |404->登录错误 |错误 |otherExn->提升otherExn 返回消息 } 这似乎有效。但是,作为F的新手,我想知道在这种情况下,是否有更优雅或更惯用的方法来捕捉内部异常?

我有时使用a来捕捉主要异常或内部异常:

let rec | NestedApiException | | e:exn= 匹配 |空->无 | :? ApiException as e->Some e |e->|嵌套的异常| | e.InnerException 然后像这样使用它:

异步的{ 尝试 ... 具有 |嵌套的异常e-> ... |otherExn-> 提出其他建议 } 我有时使用一个函数来捕捉主异常或内部异常:

let rec | NestedApiException | | e:exn= 匹配 |空->无 | :? ApiException as e->Some e |e->|嵌套的异常| | e.InnerException 然后像这样使用它:

异步的{ 尝试 ... 具有 |嵌套的异常e-> ... |otherExn-> 提出其他建议 }
基于Tarmil的活动模式的解决方案非常好,如果我想在一个文件或项目的多个位置捕获异常,我会使用它。然而,如果我只想在一个地方做这件事,那么我可能不想为它定义一个单独的活动模式

在try中使用when子句有一种更好的方式来编写您所拥有的内容。。。带着表情:

let authUserAsync (client: Client) model =
  async {
    try
      // (...)
    with baseExn when (baseExn.InnerException :? ApiException) ->
      let e = baseExn :?> ApiException
      match e.StatusCode with
      | 404 -> return LoginError
      | _ -> return ApiError }

这有点重复,因为您必须使用:?然后再次使用:?>进行转换,但这是一种更好的内联方式。

基于Tarmil的活动模式的解决方案非常好,如果我想在一个文件或项目的多个位置捕获异常,我会使用它。然而,如果我只想在一个地方做这件事,那么我可能不想为它定义一个单独的活动模式

在try中使用when子句有一种更好的方式来编写您所拥有的内容。。。带着表情:

let authUserAsync (client: Client) model =
  async {
    try
      // (...)
    with baseExn when (baseExn.InnerException :? ApiException) ->
      let e = baseExn :?> ApiException
      match e.StatusCode with
      | 404 -> return LoginError
      | _ -> return ApiError }

这有点重复,因为您必须使用:?然后再次使用:?>进行转换,但这是一种更好的内联方式。

作为替代方法,如果使用F异常,则可以使用产品类型可用的全部模式匹配。错误处理代码的读取要简洁得多

exception TimeoutException
exception ApiException of message: string * inner: exn

try
    action ()
with
| ApiException (_, TimeoutException) ->
    printfn "Timed out"
| ApiException (_, (:? ApplicationException as e)) ->
    printfn "%A" e

另外,如果使用F异常,则可以使用产品类型可用的全套模式匹配。错误处理代码的读取要简洁得多

exception TimeoutException
exception ApiException of message: string * inner: exn

try
    action ()
with
| ApiException (_, TimeoutException) ->
    printfn "Timed out"
| ApiException (_, (:? ApplicationException as e)) ->
    printfn "%A" e

我喜欢这样,但会使用reraise而不是raise otherExn。但不能在异步中使用reraise。但是仔细想想,您也可以跳过这个分支,它将被async.interest重新调用。我不知道,谢谢大家的意见。在我的情况下,我可能需要抓住这个例外
在多个地方,所以我要用@Tarmil来做这一个。我喜欢这个,但是会使用reraise而不是raiseotherexn。但是你不能在异步中使用reraise。但是仔细想想,您也可以跳过这个分支,它将被async.interest重新调用。我不知道,谢谢大家的意见。在我的情况下,我可能需要在多个地方捕捉这个异常,所以我将使用@Tarmil来处理这个异常。