9

我知道 await 不能在 catch 子句中使用。但是我还没有真正遇到过与此相关的问题,直到现在......

基本上我有一个层负责接收传入的请求,处理它们,从它们构建消息并将消息传递给另一个负责发送消息的层。

如果在消息发送过程中出现问题,则会抛出自定义异常,并被消息发送层捕获。此时,该消息的失败记录应插入 DB(需要一些时间,因此异步),并将异常传播到负责向发出请求的客户端发送错误响应的上层.

下面是一些非常简化的代码,用于说明目的:

public async Task ProcessRequestAsync(Request req)
{
    int statusCode = 0;

    try
    {       
       await SendMessageAsync(new Message(req));        
    }
    catch(MyCustomException ex)
    {
       statusCode = ex.ErrorCode;
    }

    await SendReponseToClientAsync(req, statusCode);
}


public async Task SendMessageAsync(Message msg)
{
    try
    {           
       // Some async operations done here
    }
    catch (MyCustomException ex)
    {       
        await InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); // CAN'T DO THAT
        throw;
    }
}

当然,请求处理层对DB一无所知,它只是负责构建消息,将消息传递给消息处理层,并向客户端发送响应(肯定或否定)。

所以我认为这是有道理的......如果发生异常,我的“业务”层希望在数据库中插入失败记录并重新抛出异常,以便“请求”处理层可以做必要的事情(在这种情况下,发送一个对客户的否定响应)。

不应该以这种方式使用异常吗?这对我来说似乎很干净,但我不能在 catch 子句中执行此操作这一事实让我认为设计中可能存在代码异味(即使我想在一层中处理异常然后将其重新抛出)上层做一些不同的处理,在我看来,这正是例外的原因)。

有什么想法吗?

谢谢 !

4

1 回答 1

6

我也遇到过几次。

正如拉斐尔评论的那样,您可以忽略以下结果InsertFailureForMessageInDbAsync

public async Task SendMessageAsync(Message msg)
{
  try
  {           
    // Some async operations done here
  }
  catch (MyCustomException ex)
  {       
    var _ = InsertFailureForMessageInDbAsync(msg, ex.ErrorCode);
    throw;
  }
}

请注意,InsertFailureForMessageInDbAsync默认情况下将忽略来自的任何异常。

您的另一个选择更复杂:

public async Task DoSendMessageAsync(Message msg)
{
  // Some async operations done here
}

public async Task SendMessageAsync(Message msg)
{
  var task = DoSendMessageAsync(msg);
  MyCustomException exception = null;
  try
  {
    await task;
    return;
  }
  catch (MyCustomException ex)
  {
    exception = ex;
  }

  await Task.WhenAll(task, InsertFailureForMessageInDbAsync(msg, exception.ErrorCode));
}

这将异步处理异常并返回Task具有真实值的 a (如果确实抛出一个AggregateExption异常,则包含两个异常)。InsertFailureForMessageInDbAsync

不幸的是,await将忽略第二个异常。如果您真的希望传递所有异常,可以将最后一行 ( await Task.WhenAll...) 替换为以下内容:

Exception exception2 = null;
try
{
  await InsertFailureForMessageInDbAsync(msg, exception.ErrorCode);
}
catch (Exception ex)
{
  exception2 = ex;
}

if (exception2 == null)
  throw new AggregateException(exception);
else
  throw new AggregateException(exception, exception2);

但这非常复杂,而且不完全是您想要重复的那种模式。如果可能的话,我会按照 Rafael 的建议忽略日志记录结果。

于 2012-12-15T03:55:32.223 回答