TransactionScope不回滚事务

这是我的事务范围源代码的当前体系结构。 第三个插件抛出.NETexception(不是SQLexception),它不会回滚前两个插入语句。 我做错了什么?

编辑:我从insert2和insert3中删除了try / catch。 我还从insert1 try / catch中删除了exception处理实用程序并放入“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); } } 

我也遇到了类似的问题。 我的问题发生是因为我在SqlCommands中使用的SqlConnection在创建TransactionScope之前已经打开,所以它从未作为事务登记在TransactionScope中。

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

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

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

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

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

这些操作需要多长时间? 如果它们中的任何一个都运行得很长,那么事务绑定 错误function可能会使您陷入困境 – 即连接已经分离。 尝试将Transaction Binding=Explicit Unbind添加到连接字符串。

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

  private static readonly string _connectionString = ConnectionString.GetDbConnection(); private const string inserttStr = @"INSERT INTO dbo.testTable (col1) VALUES(@test);"; ///  /// Execute command on DBMS. ///  /// Command to execute. 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; 

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

异常前的两次调用

默认隔离级别是可序列化的。 您可以在此处阅读有关锁和交易的更多信息