简体   繁体   English

更好的选择

[英]Catch-all better alternative

I'm developing using Asp.net MVC 4, NHibernate and Session-per-request. 我正在使用Asp.net MVC 4,NHibernate和每次请求会话进行开发。

I have a service method which updates multiple databases so the work is wrapped in a TransactionScope. 我有一个更新多个数据库的服务方法,因此工作包装在TransactionScope中。 I have discovered that the NHibernate Session is not usable outside the TransactionScope due to it not being thread safe. 我发现NHibernate会话在TransactionScope之外不可用,因为它不是线程安全的。

The code is similar to this: 代码类似于以下内容:

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

The database updates are critical to the success of the method. 数据库更新对于该方法的成功至关重要。 The email alerts are not. 电子邮件警报不是。 I don't want problems with the generation of the email alerts to prevent the database updates taking place. 我不希望在生成电子邮件警报时出现问题,以防止发生数据库更新。

My questions are: 我的问题是:

  1. Given the constraints does this look like a reasonable approach? 考虑到约束条件,这看起来像是一种合理的方法吗?
  2. I'm worried that an exception I haven't handled may be thrown when generating the email message. 我担心生成电子邮件时可能会引发未处理的异常。 This will cause the entire TransactionScope to be rolled back. 这将导致整个TransactionScope被回滚。 It feels like I want any exception to be ignored if it happens in that try block of code. 感觉好像我想如果任何异常发生在该try代码块中,都将被忽略。 However I appreciate a catch-all is a no-no so any other suggestions for making this more robust are welcome. 但是,我很欣赏包罗万象的一切,因此欢迎提出任何其他建议以使此功能更强大。

EDIT 编辑

Just to clarify my question: 只是为了澄清我的问题:

I know it would be better to generate and send the email after the TransactionScope. 我知道最好在TransactionScope之后生成并发送电子邮件。 However I am unable to do this as GenerateMailMessage() makes use of the NHibernate Session which is not safe to use outside of the TransactionScope block. 但是,我无法执行此操作,因为GenerateMailMessage()使用NHibernate会话,因此在TransactionScope块之外无法安全使用。

I guess what I was really asking is would it be defensible to change the catch statement above to a geniune catch-all (still with logging taking place) in order to provide as much protection to the critical UpdateOne() and UpdateTwo() calls as possible? 我想我真正要问的是将上面的catch语句更改为真正的catch-all(仍然进行日志记录),以便为关键的UpdateOne()和UpdateTwo()调用提供尽可能多的保护,这是合理的吗?可能?

Update 更新资料

My advice would be to try to prevent the exception from occurring. 我的建议是尝试防止异常的发生。 Failing that, a catch-all is likely the only option you have remaining. 否则,包罗万象可能是您剩下的唯一选择。 Logging all exceptions is going to be critical here. 记录所有异常在这里至关重要。


1st question: Your case isn't really a catch-all, you are catching all exceptions to query the type. 第一个问题:您的案例并非真正意义上的全部,您正在捕获所有异常以查询类型。 My only advice is to log details for the exceptions you choose to consume. 我唯一的建议是记录您选择使用的异常的详细信息。

2nd question: I would completely remove the generation of email from the scope if it is liable to fail. 第二个问题:如果可能会失败,我将从作用域中完全删除电子邮件的生成。 Once the transaction rolls back, all items will be rolled back too. 交易回滚后,所有项目也会回滚。 Create and send all the emails on successful commit. 成功提交后创建并发送所有电子邮件。

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

I'd suggest changing this around. 我建议改变这一点。 Instead of generating the email there and then... keep a list of the successfully processed items in a local List and then do all the mail sends at the end after you've committed. 而不是在那里生成电子邮件,然后...将成功处理的项目的列表保留在本地列表中,然后在提交后在末尾执行所有邮件发送。

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

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM