环境事务中的TransactionScope错误不会回滚事务

我使用这样的环境事务:


using(TransactionScope tran = new TransactionScope()) { CallAMethod1();//INSERT CallAMethod2();//INSERT tran.Complete(); } 

方法CallAMethod2(); 返回affected rows =-264因此无法插入但是第一个Insert已经提交!

我想知道如何使用ambient transaction ,如果第二种方法有多个需要内部事务的操作,我应该将这些操作放在内部事务中吗? 像这样 :

  DAL_Helper.Begin_Transaction(); //------Fill newKeysDictioanry affectedRow = DBUtilities.InsertEntityWithTrans("table2", newKeysDictioanry, DAL_Helper); if (affectedRow == 1) { if (!string.IsNullOrEmpty(sp_confirm)) { result_dt = UserTransactionDAL.Run_PostConfirm_SP(sp_PostConfirm, OBJ.ValuesKey, DAL_Helper); if (result_dt.Rows.Count > 0 && result_dt.Rows[0][0].ToString() == "0") { DAL_Helper.current_trans.Commit(); if (DAL_Helper.connectionState == ConnectionState.Open) { DAL_Helper.Close_Connection(); } return 1;// affectedRow; } else { DAL_Helper.current_trans.Rollback(); if (DAL_Helper.connectionState == ConnectionState.Open) { DAL_Helper.Close_Connection(); } return -2; } } //etc 

1)你需要检查tran.Complete(); 叫做。 如果tran.Complete(); 如果调用TransactionScope,则认为TransactionScope已成功完成。

来自MSDN

当您的应用程序完成它想要在事务中执行的所有工作时,您应该只调用一次Complete方法,以通知该事务管理器提交事务是可以接受的。 未能调用此方法将中止该事务。

tran.Complete();的调用tran.Complete(); 是通知交易经理完成交易。 实际上,事务管理器不跟踪您的Db适配器,也不知道连接中的操作是成功还是失败。 您的应用程序必须通过调用Complete来告知它

TransactionScope如何回滚交易?

要使您的交易失败,只需确保您不要调用tran.Complete(); 在你的代码中:

如果事务范围内没有发生exception(即,在TransactionScope对象的初始化和调用其Dispose方法之间),则允许进行范围参与的事务。 如果事务范围内确实发生exception,则将回滚它参与的事务。

在你的情况下,也许你可以在你的CallAMethod2();抛出exceptionCallAMethod2(); 如果您认为操作失败,那么tran.Complete(); 调用,事务将回滚。

2)您可以检查的第二件事是您的连接是否已在事务中登记。 如果未登记连接,则TransactionScope不会回滚。 可能的问题是:

  • 如果在进入事务范围之前存在连接,则不会登记: TransactionScope是否与预先存在的连接一起使用?
  • 您的基础连接不支持自动登记。

在这些情况下,您可以尝试手动登记您的连接(从上面的链接中提取):

 connection.EnlistTransaction(Transaction.Current) 

关于你的第二个问题:

如果第二种方法有多个需要内部事务的操作,我应该将这些操作放在内部事务中怎么办?

我会说这真的取决于你是否考虑你的CallAMethod2(); 作为一个自动操作,这意味着您可以直接在其他地方调用它而不将其包装在事务中。 大多数情况下,创建内部事务是有意义的,因为事务可以嵌套。 在您的情况下,建议您在CallAMethod2();使用TransactionScope CallAMethod2(); ,我们在创建新的事务范围时有一些选择:

TransactionScope类提供了几个重载的构造函数,这些构造函数接受TransactionScopeOption类型的枚举,该枚举定义了作用域的事务行为。 TransactionScope对象有三个选项:

加入环境事务,或者如果不存在,则创建一个新事务。

成为新的根范围,即启动新事务并使该事务成为其自身范围内的新环境事务。

根本不参与交易。 结果没有环境事务。

选择哪一个取决于您的应用程序。 在你的情况下,我猜你可以选择第一个选项。 以下是MSDN的示例

 void RootMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ SomeMethod(); scope.Complete(); } } void SomeMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ scope.Complete(); } } 

您可以使用范围内部和外部进行交易:

 string connectionString = ConfigurationManager.ConnectionStrings["db"].ConnectionString; var option = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(60) }; using (var scopeOuter = new TransactionScope(TransactionScopeOption.Required, option)) { using (var conn = new SqlConnection(connectionString)) { using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText="INSERT INTO Data(Code, FirstName)VALUES('A-100','Mr.A')"; cmd.Connection.Open(); cmd.ExecuteNonQuery(); } } using (var scopeInner = new TransactionScope(TransactionScopeOption.Required, option)) { using (var conn = new SqlConnection(connectionString)) { using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText="INSERT INTO Data(Code, FirstName) VALUES('B-100','Mr.B')"; cmd.Connection.Open(); cmd.ExecuteNonQuery(); } } scopeInner.Complete(); } scopeOuter.Complete(); } 

阅读Khanh TO所说的内容。 如果在外部事务范围之外打开连接,则不会登记连接。

这就是第二次失败时第一次调用没有回滚的原因。 您必须登记您的连接:

 using (TransactionScope tran = new TransactionScope(TransactionScopeOption.Required)) { connection.EnlistTransaction(Transaction.Current); CallAMethod1();//INSERT CallAMethod2();//INSERT tran.Complete(); }