【问题标题】:c# transactionscope need rollback only some changesc# transactionscope 只需要回滚一些更改
【发布时间】:2015-11-23 08:46:29
【问题描述】:

我面临以下问题: 在我的项目中,我在应用程序正在使用的同一个数据库中进行了错误登录。这意味着,如果发生错误,那么在每个 catch 中,都会将错误存储到 DB 中。 然而,问题是在使用事务时。当错误发生时,tran 回滚,但它也回滚记录的错误,就像在这种情况下:

这是公共服务,用于保存客户端更改。

public UpdateClient(client)
{
    try
    {
        TransactionScope scope0 = new TransactionScope();

        // some code

        scope0.Complete();
    }
    catch(Exception ex)
    {
        Logger.LogException(ex); //log the exception into DB
    }    
}

这是公共服务,用于多个客户端更改:

 public void UpdateClients(clients)
    {
        try
        {
            TransactionScope scope1 = new TransactionScope();
            foreach (client c in clients)
                {
                    UpdateClient(c);
                }
             scope1.Complete();
        }
        catch (Exception ex)
        {
            Logger.LogException(ex);           
        }
    }

这里发生的情况是,如果使用 UpdateClients(),并且如果 UpdateClient() 中发生错误,则它会在 UpdateClient() 捕获块中登录到 DB。 scope0 被回滚,并且不影响记录的异常。但是 scope1 也会被回滚,并且还会回滚存储在 DB 中的 UpdateClient() 捕获块中的异常。

我知道,有一些选项,例如将错误存储在不同的数据库中等等,但不幸的是,这在当前的开发状态下是不可接受的。 有什么办法,如何在不做大改动的情况下解决这个问题?

【问题讨论】:

  • 您可以收集错误信息并在回滚后将它们插入到数据库中。
  • @RenéVogt 您能否更具体一点,如何收集它们并区分直接调用 UpdateClient() 和从 UpdateClients() 调用?

标签: c# error-handling transactions rollback transactionscope


【解决方案1】:

您可以在更新时收集异常并将它们插入到事务范围之外的数据库中:

public UpdateClient(client, bool logErrors = true)
{
  try
  {
    TransactionScope scope0 = new TransactionScope();

    // some code

    scope0.Complete();
  }
  catch(Exception ex)
  {
    Logger.EnlistException(ex); // collect the exception
  }
  if (logErrors) Logger.WriteEnlistedExceptions();    
}

public void UpdateClients(clients)
{
  try
  {
     TransactionScope scope1 = new TransactionScope();
     foreach (client c in clients)
     {
       UpdateClient(c, false);
     }
     scope1.Complete();
  }
  catch (Exception ex)
  {
      Logger.EnlistException(ex);           
  }
  Logger.WriteEnlistedExceptions();
}

public partial class Logger
{
  private static List<Exception> _exceptionList = new List<Exception>();
  public static void EnlistException(Exception ex)
  {
    _exceptionList.Add(ex);
  }
  public static void WriteEnlistedExceptions()
  {
    foreach(Exception ex in _exceptionList)
      LogException(ex);
    _exceptionList.Clear();
  }
}

这当然还不是线程安全的。来自UpdateClientUpdateClients 的调用可以通过异常的堆栈跟踪来区分。

正如Micky所链接的问题的答案所示,没有办法阻止外部范围的回滚。

[编辑] 添加 logErrors 标志

【讨论】:

  • 啊,对不起,现在我明白你的意思了。但另一方面:外部事务范围真的有必要吗?如果只有一个无法更新,为什么要回滚所有客户端?
  • 但是当你直接调用 UpdateClient 时,因为这是公开的,你只会登记异常,但永远不会记录,还是我错了?
  • 外部 tran 是必要的,bisnis 案例与我在这里发布的不同。这个问题只是简化的问题。
  • 编辑了答案并在UpdateClients 方法中添加了logErrors 参数,默认值为true
  • 感谢您提供可能的解决方案,我将尝试与项目中的其他同事讨论此问题,但该标志可能不会被接受。
【解决方案2】:

所以我找到了一个可以接受的解决方案。代码将在没有事务的内部方法中实现,并将在 catch 中登记异常。那么用法如下:

internal UpdateClient(client)
{
try
  {
    // some code
  }
  catch(Exception ex)
  {
    Logger.EnlistException(ex); // collect the exception
  }
}

public UpdateClient(client)
{
  try
  {
    TransactionScope scope0 = new TransactionScope();

    //call internal UpdateClient
    /*internal*/ UpdateClient(client);

    scope0.Complete();
  }
  catch(Exception ex)
  {
    Logger.WriteEnlistedExceptions(ex); // collect and write the exception
  }     
}

public void UpdateClients(clients)
{
  try
  {
     TransactionScope scope1 = new TransactionScope();
     foreach (client c in clients)
     {
       /*interanl*/ UpdateClient(c);
     }
     scope1.Complete();
  }
  catch (Exception ex)
  {
      Logger.WriteEnlistedExceptions(ex);           
  }
}

public partial class Logger
{
  private static List<Exception> _exceptionList = new List<Exception>();
  public static void EnlistException(Exception ex)
  {
    _exceptionList.Add(ex);
  }
  public static void WriteEnlistedExceptions()
  {
    foreach(Exception ex in _exceptionList)
      LogException(ex);
    _exceptionList.Clear();
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    相关资源
    最近更新 更多