C# 通吃更好的选择

C# 通吃更好的选择,c#,asp.net-mvc-4,exception-handling,C#,Asp.net Mvc 4,Exception Handling,我正在使用Asp.NETMVC4、NHibernate和SessionPer请求进行开发 我有一个更新多个数据库的服务方法,因此工作被包装在TransactionScope中。我发现NHibernate会话在TransactionScope之外不可用,因为它不是线程安全的 代码与此类似: public void ProcessItems() { var items = itemService.GetAll(); var mailMessages = new List<MailMes

我正在使用Asp.NETMVC4、NHibernate和SessionPer请求进行开发

我有一个更新多个数据库的服务方法,因此工作被包装在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);
}
public void ProcessItems()
{
var items=itemService.GetAll();
var mailMessages=新列表();
使用(var scope=new TransactionScope())
{
foreach(项目中的var项目)
{
itemService.UpdateOne(项目);
itemService.updatewo(项目);
尝试
{
添加(itemService.GenerateMailMessage(item));
}
捕获(例外情况除外)
{
//我们不希望由于生成电子邮件而导致异常,从而阻止数据库工作
如果(ex)为无效操作异常
||ex是NullReferenceException
||ex是一个例外
||ex是一个例外
||ex是ItemNotFoundException(异常)
{
LogError(String.Format(“无法为item.Id:{0}-{1}”、item.Id、ex.Message生成电子邮件警报),log);
}
其他的
{
//对于我们不知道的异常类型,我们可以忽略rethrow
投掷;
}
}
scope.Complete()
}
mailService.SendMail(mailMessages);
}
数据库更新对方法的成功至关重要。电子邮件警报不是。我不希望电子邮件警报的生成出现问题,从而阻止数据库更新的发生

我的问题是:

  • 考虑到这些限制,这看起来是一种合理的方法吗
  • 我担心我没有处理的异常可能会在 正在生成电子邮件。这将导致整个TransactionScope 被回滚。感觉好像我希望任何异常都被忽略 如果它发生在代码块中,请尝试。但是,我很欣赏 “一刀切”是一个禁忌,所以有没有其他的建议可以让它更具吸引力 欢迎光临
  • 编辑

    我想澄清一下我的问题:

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


    我想我真正想问的是,将上面的catch语句更改为geniune-catch-all(仍在进行日志记录)以尽可能多地保护关键的UpdateOne()和updatewo()调用是否合理?

    Update

    我的建议是尽量防止异常的发生。如果不能做到这一点,则“一网打尽”可能是您剩下的唯一选择。记录所有异常将是至关重要的。
    第一个问题:您的案例并不是一个包罗万象的案例,您正在捕获所有异常以查询类型。我唯一的建议是记录您选择使用的异常的详细信息

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

    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);
      }
    }
    
    public void ProcessItems()
    {
    var items=itemService.GetAll();
    var mailMessages=新列表();
    bool-committed=false;
    使用(var scope=new TransactionScope())
    {
    foreach(项目中的var项目)
    {
    itemService.UpdateOne(项目);
    itemService.updatewo(项目);
    }
    scope.Complete()
    承诺=真实;
    }
    如果(承诺)
    {
    //在此嵌入创建代码和异常处理。
    mailService.SendMail(mailMessages);
    }
    }
    
    更新

    我的建议是尽量防止异常的发生。如果不能做到这一点,则“一网打尽”可能是您剩下的唯一选择。记录所有异常将是至关重要的。
    第一个问题:您的案例并不是一个包罗万象的案例,您正在捕获所有异常以查询类型。我唯一的建议是记录您选择使用的异常的详细信息

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

    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);
      }
    }
    
    public void ProcessItems()
    {
    var items=itemService.GetAll();
    var mailMessages=新列表();
    bool-committed=false;
    使用(var scope=new TransactionScope())
    {
    foreach(项目中的var项目)
    {
    itemService.UpdateOne(项目);
    itemService.updatewo(项目);
    }
    scope.Complete()
    承诺=真实;
    }
    如果(承诺)
    {
    //在此嵌入创建代码和异常处理。
    mailService.SendMail(mailMessages);
    }
    }
    
    我建议改变这一点。不要在那里生成电子邮件,然后……在本地列表中保留成功处理项目的列表,然后在提交后在末尾发送所有邮件

    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
    
    }
    
    public void ProcessItems()
    {
    var items=itemService.GetAll();
    var successItems=新列表();
    var mailMessages=新列表();
    使用(var scope=new TransactionScope())
    {
    foreach(项目中的var项目)
    {
    itemService.UpdateOne(项目);
    itemService.updatewo(项目);
    后续项目。添加(项目);
    //对于失败的数据库更新,您仍然需要try/catch处理……或者您希望所有更新都失败。
    }
    scope.Complete()
    }
    mailMessages=successItems.Select(i=>itemService.GenerateMailMessage.ToList();
    //处理邮件信息
    }
    
    我建议改变这一点。不要在那里生成电子邮件,然后……在本地列表中保留成功处理项目的列表,然后在提交后在末尾发送所有邮件

    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
    
    }
    
    public void ProcessItems()
    {
    var items=itemService.GetAll();
    var successItems=新的Li