2008-12-04 243 views
9

以下是我的事务范围源代码的当前体系结构。第三个插入引发了一个.NET异常(不是SQL异常),它不回滚前两个插入语句。我做错了什么?TransactionScope不回滚事务

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

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

UPDATE:根据我收到的反馈意见中,“提供SQLHelper”类使用SqlConnection对象建立与数据库的连接,然后创建一个SqlCommand对象,设置CommandType属性,以“StoredProcedure的”调用ExecuteNonQuery方法的SqlCommand。

我也没有将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); 
    } 
} 
+0

我不想质疑你的开发技能等,但你如何测试交易已经回滚?交易是否可以正常运行,但是您误解了结果。也许其他事情正在发生,我们/你在咆哮着错误的树? – 2008-12-04 23:26:43

+0

希望这有助于:http://stackoverflow.com/questions/28191333/error-in-ambient-transaction-doesnt-rollback-the-transaction/28258935#28258935 – 2015-09-06 04:02:56

回答

6

看起来你正在捕捉Insert3()中的异常,所以你的代码在调用后继续。如果你想让它回滚,你需要让异常冒泡到主例程中的try/catch块,以便ts.Complete()语句永远不会被调用。

+0

所以,我应该从插入2删除try/catch语句和3? – 2008-12-04 00:51:36

+0

是的。或重新抛出异常或其他异常。 – tvanfosson 2008-12-04 00:56:51

+0

是的,你没有处理这个异常,主叫方只是继续......也......是他们在sqlHelper中声明的任何事务?......我在一个地方提交一个问题,将其删除。 – 2008-12-04 01:03:28

1

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

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

1

(基于编辑的版本不下咽例外)

这些操作需要多长时间?如果它们中的任何一个很长时间运行,则有可能Transaction Binding错误功能已咬住您 - 即连接已分离。尝试将Transaction Binding=Explicit Unbind添加到连接字符串。

23

我也遇到过类似的问题。我的问题发生是因为在我的SqlCommands中使用的SqlConnection在TransactionScope创建之前已经打开,所以它从来没有作为事务入账到TransactionScope中。

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

0

我没有看到你的帮助程序类,但事务范围回滚如果你没有调用完整的语句,即使你从.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。

two calls before exception

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