c# - TransactionScope:避免分布式事务

标签 c# transactions transactionscope distributed-transactions

我有一个父对象(DAL 的一部分),其中包含子对象的集合 ( List<t>)。

当我将对象保存回数据库时,我输入/更新父对象,然后遍历每个子对象。为了便于维护,我已将子项的所有代码放入一个单独的私有(private)方法中。

我打算使用标准的 ADO 事务,但在我的旅行中,我偶然发现了 TransactionScope 对象,我相信这将使我能够将所有 DB 交互包装在父方法中(连同子方法中的所有交互)一笔交易。

到目前为止一切顺利..?

所以下一个问题是如何在这个 TransactionScope 中创建和使用连接。我听说使用多个连接,即使它们连接到同一个数据库也会迫使 TransactionScope 认为它是一个分布式事务(涉及一些昂贵的 DTC 工作)。

是这样吗?或者,正如我似乎在其他地方读到的那样,使用相同的连接字符串(这将有助于连接池)会很好吗?

更具体地说,我是否...

  1. 在父子中创建单独的连接(尽管使用相同的连接字符串)
  2. 在父级中创建一个连接并将其作为参数传递(对我来说似乎很笨拙)
  3. 做点别的……?

更新:

虽然看起来我可以使用常用的 .NET3.5+ 和 SQL Server 2008+,但该项目的另一部分将使用 Oracle (10g),所以我不妨练习一种可以跨项目一致使用。

所以我将简单地将连接传递给子方法。


选项 1 代码示例:

using (TransactionScope ts = new TransactionScope())
            {
                using (SqlConnection conn = new SqlConnection(connString))
                {
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cmd.Connection = conn;
                        cmd.Connection.Open();
                        cmd.CommandType = CommandType.StoredProcedure;

                        try
                        {
                            //create & add parameters to command

                            //save parent object to DB
                            cmd.ExecuteNonQuery();

                            if ((int)cmd.Parameters["@Result"].Value != 0)
                            {
                                //not ok
                                //rollback transaction
                                ts.Dispose();
                                return false;
                            }
                            else //enquiry saved OK
                            {
                                if (update)
                                {
                                    enquiryID = (int)cmd.Parameters["@EnquiryID"].Value;
                                }

                                //Save Vehicles (child objects)
                                if (SaveVehiclesToEPE())
                                {
                                    ts.Complete();
                                    return true;
                                }
                                else
                                {
                                    ts.Dispose();
                                    return false;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            //log error
                            ts.Dispose();
                            throw;
                        }
                    }
                }
            }

最佳答案

当您使用 TransactionScope 跨多个连接进行交易时,许多数据库 ADO 提供程序(例如 Oracle ODP.NET)确实会开始分布式事务 - 即使它们共享相同的连接字符串。

某些提供程序(如 .NET 3.5+ 中的 SQL2008)会识别何时在引用相同连接字符串的事务范围内创建新连接,并且不会导致 DTC 工作。但是连接字符串中的任何变化(例如调整参数)都可能会阻止这种情况的发生 - 并且行为将恢复为使用分布式事务。

不幸的是,确保您的事务在不创建分布式事务的情况下协同工作的唯一可靠方法是将连接对象(或 IDbTransaction)传递给需要“继续”相同的方法交易。

有时它有助于将连接提升到您正在工作的类的成员,但这会造成尴尬的情况 - 并使控制连接对象的生命周期和处置变得复杂(因为它通常会排除使用using 语句)。

关于c# - TransactionScope:避免分布式事务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3187632/

相关文章:

c# - 在 Jquery 中使用 c# 变量

safari - TransactionInactiveError : Failed to execute 'get' on 'IDBObjectStore' : The transaction is inactive or finished

qt多个QSqlTableModels在一个事务中一起编辑

c# - 有没有办法打开一个不参与当前 TransactionScope 的新连接?

c# - 使用掩码在 C# 中进行位操作

c# - DataGridView:摆脱单元格验证

c# - 即使有一个以上的空格,也应该只用四个斜杠代替

java - 在 JUnit 中使用 Spring 测试服务时如何回滚数据库事务?

c# - 为什么这个交易范围不起作用?

c# - 交易范围和手动定义的交易之间的区别?