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について何も知りません。メッセージを作成し、メッセージをメッセージ処理レイヤーに渡し、クライアントに応答(肯定的または否定的)を送信するだけです。

だから私はそれが理にかなっていると思います...例外が発生した場合、私の「ビジネス」レイヤーはDBに失敗レコードを挿入し、「リクエスト」処理レイヤーが必要なことを実行できるように例外を再スローします(このコンテキストでは、クライアントへの否定的な応答)。

例外をこのように使用すべきではありませんか? 私にはきれいに思えますが、catch 句でこれを待機できないという事実は、設計にコードの匂いがあると思わせます (1 つのレイヤーで例外を処理し、それを再スローするという考えがあっても)いくつかの異なる処理を行うための上位層も、まさに例外が作成されているように思えます)。

これについて何か考えはありますか?

ありがとう !

4

1 に答える 1

6

私もこれに数回遭遇しました。

Rafael がコメントしたように、次の結果は無視できます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を返します ( 1 つをスローAggregateExptionする場合は両方の例外を含みます)。InsertFailureForMessageInDbAsync

残念ながら、await2 番目の例外は無視されます。本当にすべての例外を通過させたい場合は、最後の行 ( 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 に答える