TransactionScope:避免分布式事务

CJM*_*CJM 19 c# transactions transactionscope distributed-transactions

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

当我将对象保存回DB时,我输入/更新父对象,然后循环遍历每个子对象.为了可维护性,我将孩子的所有代码放入一个单独的私有方法中.

我打算使用标准的ADO事务,但在我的旅行中,我偶然发现了TransactionScope对象,我相信这将使我能够在一个事务中将父方法中的所有数据库交互(以及子方法中的所有交互)包装起来.

到现在为止还挺好..?

所以接下来的问题是如何在TransactionScope中创建和使用连接.我听说使用多个连接,即使它们是相同的DB也可以强制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;
                        }
                    }
                }
            }
Run Code Online (Sandbox Code Playgroud)

LBu*_*kin 24

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

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

遗憾的是,确保您的事务在不创建分布式事务的情况下协同工作的唯一可靠方法是将连接对象(或IDbTransaction)传递给需要在同一事务上"继续"的方法.

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