抓住所有更好的选择

时间:2013-09-02 13:37:28

标签: c# asp.net-mvc-4 exception-handling

我正在开发使用Asp.net MVC 4,NHibernate和Session-per-request。

我有一个更新多个数据库的服务方法,因此工作包含在TransactionScope中。我发现NHibernate Session在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代码块中。不过我很感激 catch-all是一个禁忌,所以任何其他的建议,使这更多 我们很欢迎。
  3. 修改

    只是为了澄清我的问题:

    我知道在TransactionScope之后生成并发送电子邮件会更好。但是我无法这样做,因为GenerateMailMessage()使用了在TransactionScope块之外使用不安全的NHibernate会话。

    我想我真正想要的是将上面的catch语句更改为geniune catch-all(仍然使用日志记录)是否可以防御,以便为关键的UpdateOne()和UpdateTwo提供尽可能多的保护( )尽可能打电话?

2 个答案:

答案 0 :(得分:3)

<强>更新

我的建议是尽量防止异常发生。如果做不到这一点,一个全能的可能是你剩下的唯一选择。记录所有异常在这里至关重要。

<小时/> 第一个问题:你的案例并不是真正的全部,你正在捕获查询类型的所有异常。我唯一的建议是记录您选择使用的例外的详细信息。

第二个问题:如果可能会失败,我会完全删除范围内的电子邮件。事务回滚后,所有项目也将回滚。在成功提交时创建并发送所有电子邮件。

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);
  }
}

答案 1 :(得分: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

}