在catch子句中等待禁止.寻找工作环绕

dar*_*key 9 c# exception-handling async-await c#-5.0

我知道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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

所以我认为这是有意义的......如果发生异常,我的"业务"层想要在DB中插入失败记录并重新抛出异常,以便"请求"处理层可以做必要的事情(在此上下文中,发送一个消极反应回到客户端).

是否应该以这种方式使用例外情况?这对我来说似乎很干净但是我在catch子句中无法做到这一点的事实让我觉得设计中可能存在代码味道(即使我想在一层中处理异常然后重新抛出它用于上层做了一些不同的处理,对我来说听起来就像它正是为什么做的例外).

这有什么想法吗?

谢谢 !

Ste*_*ary 6

我也遇到过这种情况.

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

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

请注意,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));
}
Run Code Online (Sandbox Code Playgroud)

这将异步处理异常并返回一个Task具有real的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);
Run Code Online (Sandbox Code Playgroud)

但这很复杂,并不完全是你想重复的那种模式.如果可能的话,我会忽略Rafael建议的记录结果.