0

Asp.net MVC 4、NHibernate、Session-per-request を使用して開発しています。

複数のデータベースを更新するサービス メソッドがあるため、作業は TransactionScope でラップされます。NHibernate セッションはスレッド セーフではないため、TransactionScope の外では使用できないことがわかりました。

コードは次のようになります。

public void ProcessItems()
{
  var items = itemService.GetAll();
  var mailMessages = new List<MailMessage>();
  using(var scope = new TransactionScope())
  {
    foreach(var item in items)
    {
      itemService.UpdateOne(item);
      itemService.UpdateTwo(item);
      try 
      {
        mailMessages.Add(itemService.GenerateMailMessage(item));
      }
      catch(Exception ex)
      {
        // we don't want exceptions caused be generating email to prevent DB work
        if (ex is InvalidOperationException
          || ex is NullReferenceException
          || ex is FormatException
          || ex is ArgumentException
          || ex is ItemNotFoundException)
        {
          LogError(String.Format("Unable to generate email alert for item.Id:{0} - {1}", item.Id, ex.Message), log);                                
        }
        else
        {
          // For exception types we don't know we can ignore rethrow
          throw;
        }
    }
    scope.Complete()
  }
  mailService.SendMail(mailMessages);
}

データベースの更新は、メソッドの成功にとって重要です。メールアラートはそうではありません。データベースの更新が行われないように、電子メール アラートの生成に問題が発生することは望ましくありません。

私の質問は次のとおりです。

  1. 制約を考えると、これは合理的なアプローチのように見えますか?
  2. 電子メール メッセージを生成するときに、処理していない例外がスローされるのではないかと心配しています。これにより、TransactionScope 全体がロールバックされます。コードの try ブロックで例外が発生した場合、例外を無視したいような気がします。ただし、キャッチオールはノーノーであることを理解しています。これをより堅牢にするための他の提案は大歓迎です。

編集

私の質問を明確にするために:

TransactionScope の後に電子メールを生成して送信する方がよいことはわかっています。ただし、GenerateMailMessage() は、TransactionScope ブロックの外部で使用するのは安全ではない NHibernate セッションを使用するため、これを行うことはできません。

私が本当に求めていたのは、重要な UpdateOne() および UpdateTwo() 呼び出しにできるだけ多くの保護を提供するために、上記の catch ステートメントを本物のキャッチオール (まだログが行われている状態で) に変更することは弁護できるだろうかということだと思います。可能?

4

2 に答える 2

3

アップデート

私のアドバイスは、例外が発生しないようにすることです。それができない場合、残されている唯一のオプションはおそらくキャッチオールです。ここでは、すべての例外をログに記録することが重要になります。


1 番目の質問: あなたのケースは実際にはキャッチオールではありません。型をクエリするためにすべての例外をキャッチしています。私の唯一のアドバイスは、使用することを選択した例外の詳細をログに記録することです。

2 番目の質問: 失敗する可能性がある場合は、電子メールの生成をスコープから完全に削除します。トランザクションがロールバックすると、すべてのアイテムもロールバックされます。コミットが成功したら、すべてのメールを作成して送信します。

public void ProcessItems()
{
  var items = itemService.GetAll();
  var mailMessages = new List<MailMessage>();
  bool committed = false;

  using(var scope = new TransactionScope())
  {
    foreach(var item in items)
    {
      itemService.UpdateOne(item);
      itemService.UpdateTwo(item);
    }
    scope.Complete()
    committed = true;
  }

  if (committed)
  {
    // Embed creation code and exception handling here.

    mailService.SendMail(mailMessages);
  }
}
于 2013-09-02T13:40:20.887 に答える
0

これを変更することをお勧めします。そこでメールを生成してから... 正常に処理されたアイテムのリストをローカルリストに保持し、コミット後に最後にすべてのメール送信を行います。

public void ProcessItems()
{
  var items = itemService.GetAll();
  var successItems = new List<Item>();
  var mailMessages = new List<MailMessage>();
  using(var scope = new TransactionScope())
  {
    foreach(var item in items)
    {
      itemService.UpdateOne(item);
      itemService.UpdateTwo(item);
      successItems.Add(item);

// you still need try/catch handling for DB updates that fail... or maybe you want it all to fail.
    }
    scope.Complete()
  }

  mailMessages = successItems.Select(i => itemService.GenerateMailMessage).ToList();

  //Do stuff with mail messages

}
于 2013-09-02T13:44:48.113 に答える