【问题标题】:Is this TRANSACTION being ROLLBACK(ed) for me?这个交易对我来说是回滚(ed)吗?
【发布时间】:2013-10-21 11:10:58
【问题描述】:

如果我尝试创建现有表导致错误,则现有事务似乎已经自行回滚:

private void CreateSomeThings()
{
    SqlConnection SqlConn = new SqlConnection(ConnectionString);
    SqlConn.Open();

    (new SqlCommand("BEGIN TRANSACTION", SqlConn)).ExecuteNonQuery();

    try
    {
        (new SqlCommand("CREATE TABLE sometable ([some_id] [int] IDENTITY(1,1) NOT NULL)", SqlConn)).ExecuteNonQuery();

        // Create the table again, but carry on by catching the exception
        try
        {
            (new SqlCommand("CREATE TABLE sometable ([some_id] [int] IDENTITY(1,1) NOT NULL)", SqlConn)).ExecuteNonQuery();
        }
        catch (Exception)
        {
        }

        // If another exception is thrown
        (new SqlCommand("bingy bongy boo", SqlConn)).ExecuteNonQuery();

        (new SqlCommand("COMMIT TRANSACTION", SqlConn)).ExecuteNonQuery();
    }
    catch (Exception Ex)
    {
        try
        {
            // ... then this command will fail with "no corresponding BEGIN TRANSACTION"
            (new SqlCommand("ROLLBACK TRANSACTION", SqlConn)).ExecuteNonQuery();
        }
        catch (Exception Ex2)
        {
            throw;
        }
    }
}

我想了解发生了什么以及为什么。我希望事务回滚是我的责任 - 对于其他错误,它不会这样做:例如,如果我只是调用“bingy bongy”,则只有调用会引发异常,然后我在异常中 ROLLBACK 没有任何问题。

【问题讨论】:

  • 你怎么知道的?究竟是什么问题?
  • 请看代码cmets。 ROLLBACK TRANSACTION 在异常处理程序中失败。
  • 问题是“我不明白”,我想了解发生了什么以及为什么。我希望事务回滚是我的责任 - 对于其他错误,它不会为我这样做。
  • 您在此处的每个查询都是单独的事务,因此您的回滚不会起作用。您需要使用事务对象或更通用的函数在 BEGIN/ROLLBACK/COMMIT 事务中使用 errorraise() 和错误捕获 SQL SIDE 包装事务 SQL。
  • 可能是这样,但是如果我将代码更改为使用事务对象,我会遇到同样的问题:如果您尝试创建一个已经存在的表,则事务对象将被关闭/回滚代表我。如果我犯了一些其他错误(例如调用bingy bongy boo),事务仍然有效,现在负责回滚。对我来说似乎不太一致。

标签: c# sql-server transactions sqltransaction


【解决方案1】:

SQL Server 可以单方面决定回滚您的事务。这是 SQL Server 中的一个严重设计缺陷,因为您的应用程序永远无法知道事务是否仍处于活动状态。没有很好地记录哪些类型的错误回滚以及哪些类型的错误不回滚。例如,我想我记得唯一键违规和其他数据错误不会回滚。但其他人会。有些错误甚至会终止连接(这种情况很少见,不是设计缺陷)。

我建议您以这样一种方式编码,即在第一个错误时中止事务,然后失败或重试所有操作。这样可以省去很多麻烦。最好每批执行一条语句,否则您可能会在事务之外运行第二条语句。

如果你真的想继续追查错误,你必须做两件事:

  1. 建立不回滚的错误白名单。在这种情况下,您可以继续前进。
  2. SELECT @@TRANCOUNT 检查交易是否仍然有效。

【讨论】:

  • 通过每批执行一条语句,您是否有效地建议不要使用事务?
  • 我认为您在这里混淆了一些术语,尽管我不确定哪些术语。一个连接可以有多个事务,可以有多个批处理,可以有多个语句。每个ExecuteNonQuery 调用都是一个批处理,在其CommandText 中可以有多个语句。使用事务,但如果可能,每批一个语句。
【解决方案2】:

您需要将事务对象传递给您正在使用的每个命令,以使它们参与同一事务。

通常的模式是:

using (var conn = new SqlConnection("your connection string here"))
{
    SqlTransaction trans = null;
    try
    {
        conn.Open();
        trans = conn.BeginTransaction();

        using (SqlCommand command = new SqlCommand("command text here", conn, trans))
        {
            // do your job
        }
        trans.Commit();
    }
    catch (Exception ex)
    {
        try
        {
            // Attempt to roll back the transaction.
            if (trans != null) trans.Rollback();
        }
        catch (Exception exRollback)
        {
            // Throws an InvalidOperationException if the connection  
            // is closed or the transaction has already been rolled  
            // back on the server.
        }
    }
}

【讨论】:

  • 如果我使用 C# connection.BeginTransaction 而不是 Transaction 变得无效(对象内部的 Connection 变为 null) - 即看起来表现相同。
  • 如果该事务的一部分是创建一个已经存在的表,您的事务将不再有效 - 如果您然后尝试执行 trans.Rollback()我>。我的问题是……为什么?
  • 该事务已在服务器上回滚。尝试在 SSMS 中运行相同的操作 - 如果事务内部的查询中有错误,则会回滚。我更新了我的答案以反映这一点。
  • 但是“bingy bongy boo”几乎不是有效的 SQL,但如果这是我唯一的调用,那不会导致回滚......为什么不一致?
  • 不确定那个。可能是因为它不是一个有效的 sql 并且没有什么可以回滚的?
猜你喜欢
  • 2011-08-14
  • 2012-07-10
  • 1970-01-01
  • 2011-11-23
  • 2011-08-17
  • 2017-05-11
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多