【问题标题】:TransactionScope not rolling back transactionTransactionScope 不回滚事务
【发布时间】:2008-12-04 00:42:27
【问题描述】:

这是我的事务范围源代码的当前架构。第三次插入会引发 .NET 异常(不是 SQL 异常),并且不会回滚前两个插入语句。我做错了什么?

编辑: 我从 insert2 和 insert3 中删除了 try/catch。我还从 insert1 try/catch 中删除了异常处理实用程序并放入“throw ex”。它仍然不回滚事务。

编辑 2: 我在 Insert3 方法中添加了 try/catch,并在 catch 语句中添加了一个“throw”。它仍然不回滚事务。

更新:根据我收到的反馈,“SqlHelper”类是使用SqlConnection对象建立与数据库的连接,然后创建一个SqlCommand对象,将CommandType属性设置为“StoredProcedure " 并调用 SqlCommand 的 ExecuteNonQuery 方法。

我也没有将 Transaction Binding=Explicit Unbind 添加到当前连接字符串。我将在下一次测试中添加它。

public void InsertStuff()
{
    try
    {
        using(TransactionScope ts = new TransactionScope())
        {
            //perform insert 1
            using(SqlHelper sh = new SqlHelper())
            {
                SqlParameter[] sp = { /* create parameters for first insert */ };

                sh.Insert("MyInsert1", sp);
            }

            //perform insert 2
            this.Insert2();

            //perform insert 3 - breaks here!!!!!
            this.Insert3();

            ts.Complete();            
        }
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

public void Insert2()
{
    //perform insert 2
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /* create parameters for second insert */ };

        sh.Insert("MyInsert2", sp);
    }
}

public void Insert3()
{
    //perform insert 3
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /*create parameters for third insert */ };

        sh.Insert("MyInsert3", sp);
    }
}

【问题讨论】:

  • 我不想怀疑您的开发技能等,但是您如何测试事务已回滚?交易是否可能正常工作,但您误解了结果。也许发生了其他事情,我们/你在找错树?
  • 希望对您有所帮助:stackoverflow.com/questions/28191333/…

标签: c# .net transactions transactionscope


【解决方案1】:

我也遇到过类似的问题。出现我的问题是因为我在 SqlCommands 中使用的 SqlConnection 在创建 TransactionScope 之前已经打开,因此它从未作为事务加入 TransactionScope。

SqlHelper 类是否可能重用了在您进入 TransactionScope 块之前打开的 SqlConnection 实例?

【讨论】:

    【解决方案2】:

    看起来您在 Insert3() 中捕获了异常,因此您的代码在调用后继续。如果您希望它回滚,您需要让异常冒泡到主例程中的 try/catch 块,以便永远不会调用 ts.Complete() 语句。

    【讨论】:

    • 那么,我应该从插入 2 和 3 中删除 try/catch 语句吗?
    • 是的。或者重新抛出异常或另一个异常。
    • 是的,您没有处理调用方正在继续的异常......还有......他们是否在 sqlHelper 中声明了任何事务?......我在提交问题时提交了一个问题在我的助手中声明的事务,必须删除它。
    • 不,我没有在 sqlhelper 类中声明任何事务
    【解决方案3】:

    只有在未调用 ts.complete 的情况下退出 using 时才会发生隐式回滚。因为您在 Insert3() 中处理异常,所以该异常永远不会导致 using 语句退出。

    要么重新抛出异常,要么通知调用者需要回滚(将 Insert3() 的签名更改为 bool Insert3()?)

    【讨论】:

      【解决方案4】:

      (基于不吞下异常的编辑版本)

      操作需要多长时间?如果它们中的任何一个运行时间很长,则Transaction Binding bug 功能可能已经咬到你了 - 即连接已经断开。尝试将Transaction Binding=Explicit Unbind 添加到连接字符串中。

      【讨论】:

      • 没有骰子。我在连接字符串中添加了显式取消绑定,但它仍然不回滚
      【解决方案5】:

      我没有看到您的帮助程序类,但如果您不调用完整语句,即使您从 .NET 代码中收到错误,事务范围也会回滚。我为你复制了一个例子。您可能在调试时做错了什么。此示例在 .net 代码中存在错误,并且与您的类似 catch 块。

        private static readonly string _connectionString = ConnectionString.GetDbConnection();
      
          private const string inserttStr = @"INSERT INTO dbo.testTable (col1) VALUES(@test);";
      
              /// <summary>
              /// Execute command on DBMS.
              /// </summary>
              /// <param name="command">Command to execute.</param>
              private void ExecuteNonQuery(IDbCommand command)
              {
                  if (command == null)
                      throw new ArgumentNullException("Parameter 'command' can't be null!");
      
                  using (IDbConnection connection = new SqlConnection(_connectionString))
                  {
                      command.Connection = connection;
                      connection.Open();
                      command.ExecuteNonQuery();
                  }
              }
      
              public void FirstMethod()
              {
                  IDbCommand command = new SqlCommand(inserttStr);
                  command.Parameters.Add(new SqlParameter("@test", "Hello1"));
      
      
                      ExecuteNonQuery(command);
      
              }
      
              public void SecondMethod()
              {
                  IDbCommand command = new SqlCommand(inserttStr);
                  command.Parameters.Add(new SqlParameter("@test", "Hello2"));
      
      
                      ExecuteNonQuery(command);
      
              }
      
              public void ThirdMethodCauseNetException()
              {
                  IDbCommand command = new SqlCommand(inserttStr);
                  command.Parameters.Add(new SqlParameter("@test", "Hello3"));
      
      
                      ExecuteNonQuery(command);
                  int a = 0;
                  int b = 1/a;
      
              }
      
          public void MainWrap()
          {
      
      
              TransactionOptions tso = new TransactionOptions();
              tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
              //TransactionScopeOption.Required, tso
              try
              {
                  using (TransactionScope sc = new TransactionScope())
                  {
                      FirstMethod();
                      SecondMethod();
                      ThirdMethodCauseNetException();
                      sc.Complete();
                  }
              }
              catch (Exception ex)
              {
                  logger.ErrorException("eee ",ex);
      
              }
          }
      

      如果你想调试你的事务,你可以使用这个脚本来查看锁和等待状态等。

      SELECT 
      request_session_id AS spid,
      CASE transaction_isolation_level 
      WHEN 0 THEN 'Unspecified' 
      WHEN 1 THEN 'ReadUncomitted' 
      WHEN 2 THEN 'Readcomitted' 
      WHEN 3 THEN 'Repeatable' 
      WHEN 4 THEN 'Serializable' 
      WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL ,
      resource_type AS restype,
      resource_database_id AS dbid,
      DB_NAME(resource_database_id) as DBNAME,
      resource_description AS res,
      resource_associated_entity_id AS resid,
      CASE 
      when resource_type = 'OBJECT' then OBJECT_NAME( resource_associated_entity_id) 
      ELSE 'N/A'
      END as ObjectName,
      request_mode AS mode,
      request_status AS status
      FROM sys.dm_tran_locks l
      left join sys.dm_exec_sessions s on l.request_session_id = s.session_id
      where resource_database_id = 24
      order by spid, restype, dbname;
      

      在调用异常方法之前,您将看到两个方法调用的一个 SPID。

      默认隔离级别是可序列化的。You can read more about locks and transactions here

      【讨论】:

        【解决方案6】:

        当我在TransactionScope 中调用 WCF 服务操作时遇到了类似的问题。 我注意到由于服务接口中的“TransactionFlow”属性,不允许事务流。因此,WCF 服务操作没有使用外部事务范围使用的事务。更改它以允许如下所示的事务流解决了我的问题。

        [TransactionFlow(TransactionFlowOption.NotAllowed)]

        到

        [TransactionFlow(TransactionFlowOption.Allowed)]

        【讨论】:

          猜你喜欢
          • 2010-10-04
          • 2015-03-27
          • 2011-04-26
          • 2014-05-30
          • 1970-01-01
          • 2012-05-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多