像TransactionScope一样使用DbTransaction

System.Transactions.TransactionScope为了提供一种非常方便的实现分布式事务的方式,但是在某些情况下为了阻止本地事务向分布式事务提升,我们只能通过DbTransaction进行事务控制。在进行数据库操作的时候,我们经常会遇到这样的事务场景:当某个方法单独执行的时候,它应该在一个单一的DbTransaction中执行;当多个方法一起执行的时候,它们应用共同在DbTransaction中执行。在本篇文章中我们通过对DbTransaction进行封装,提供一种类似于TransactionScope的编程方式来解决这个问题。[源代码从这里下载]

目录
一、自定义的Transaction
二、自定义CommittableTransaction和DependentTransaction
三、自定义TransactionScope
四、一个具有事务感知的DbHelper
五、三种事务控制的性能

一、自定义的Transaction

我们完全采用System.Transactions的应用编程接口的设计定义基于DbTransaction的相关类型,首选来看看具有如下定义的表示事务的Transaction类型。Transaction是一个抽象类,具有DbTransactionWrapper和Completed两个属性,前者表示对DbTransaction的封装,后者表示事务操作是否已经完成。静态属性Current表示当前事务,这是一个基于当前线程的静态字段。Rollback和Dispose直接调用了DbTransactionWrapper的同名方法。

   1: namespace Artech.Transactions
   2: {
   3:     public class DbTransactionWrapper: IDisposable
   4:     {
   5:         public DbTransactionWrapper(DbTransaction transaction)
   6:         {
   7:             this.DbTransaction = transaction;
   8:         }
   9:         public DbTransaction DbTransaction { get; private set; }
  10:         public bool IsRollBack { get; set; }
  11:         public void Rollback()
  12:         {
  13:             if (!this.IsRollBack)
  14:             {
  15:                 this.DbTransaction.Rollback();
  16:             }
  17:         }
  18:         public void Commit()
  19:         {
  20:             this.DbTransaction.Commit();
  21:         }
  22:         public void Dispose()
  23:         {
  24:             this.DbTransaction.Dispose();
  25:         }
  26:     }
  27:     public abstract class Transaction : IDisposable
  28:     {
  29:         [ThreadStatic]
  30:         private static Transaction current;
  31:  
  32:         public bool Completed { get; private set; }
  33:         public DbTransactionWrapper DbTransactionWrapper { get; protected set; }
  34:         protected Transaction() { }
  35:         public void Rollback()
  36:         {
  37:             this.DbTransactionWrapper.Rollback();
  38:         }
  39:         public DependentTransaction DependentClone()
  40:         {
  41:             return new DependentTransaction(this);
  42:         }
  43:         public void Dispose()
  44:         {
  45:             this.DbTransactionWrapper.Dispose();
  46:         }
  47:         public static Transaction Current
  48:         {
  49:             get { return current; }
  50:             set { current = value; }
  51:         }
  52:     }
  53: }

二、自定义CommittableTransaction和DependentTransaction

和定义在System.Transactions的事务一样,具体事务分为CommittableTransaction和DependentTransaction,前者可以使一个可以直接提交的独立事务,后者则是依附于某个事务的依赖事务。如下面的代码片断所示,我们直接基于某个DbTransaction来创建CommittableTransaction对象。而用于提交事务的Commit方法则直接调用DbTransaction的同名方法。

   1: public class CommittableTransaction : Transaction
   2: {
   3:     public CommittableTransaction(DbTransaction dbTransaction)
   4:     {
   5:         this.DbTransaction = dbTransaction;
   6:     }
   7:     public void Commit()
   8:     {
   9:         this.DbTransaction.Commit();
  10:     }
  11: }

下面是DependentTransaction的定义,我们直接基于某个作为内部事务的Transaction对象来创建DependentTransaction,而这个内部事务可以是一个CommittableTransaction,也可以是DependentTransaction。由于DependentTransaction只具有一个内部构造函数,所以DependentTransaction通过调用定义在Transaction的DependentClone方法来创建。

   1: public class DependentTransaction : Transaction
   2: {
   3:     public Transaction InnerTransaction { get; private set; }
   4:     internal DependentTransaction(Transaction innerTransaction)
   5:     {
   6:         this.InnerTransaction = innerTransaction;
   7:         this.DbTransaction = this.InnerTransaction.DbTransaction;
   8:     }
   9: }

三、自定义TransactionScope

我们在进行事务编程的时候只会使用到具有如下定义的TransactionScope类型。我们通过指定连接字符串名称、隔离级别以及用于创建DbProviderFactory的委托创建TransactionScope对象。在TransactionScope的构造函数中,如果通过Artech.Transactions.Transaction.Current属性表示的当前事务不存在,则根据DbProviderFactory创建DbConnection并调用BeginTransaction方法开启事务,并用被开启的DbTransaction创建CommittableTransaction对象。最终将创建的CommittableTransaction作为当前事务;服务过当前事务已经存在,则直接调用它的DependentClone方法创建的DependentTransaction作为当前事务。

   1: namespace Artech.Transactions
   2: {
   3:     public class TransactionScope: IDisposable
   4:     {
   5:         private Transaction transaction = Transaction.Current;
   6:         public bool Completed { get; private set; }
   7:  
   8:         public TransactionScope(string connectionStringName, IsolationLevel isolationLevel = IsolationLevel.Unspecified,
   9:             Func<string, DbProviderFactory> getFactory = null)
  10:         {
  11:             if (null == transaction)
  12:             {
  13:                 if (null == getFactory)
  14:                 {
  15:                     getFactory = cnnstringName => DbHelper.GetFactory(cnnstringName);
  16:                 }
  17:                 DbProviderFactory factory = getFactory(connectionStringName);
  18:                 DbConnection connection = factory.CreateConnection();
  19:                 connection.ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
  20:                 connection.Open();
  21:                 DbTransaction dbTransaction = connection.BeginTransaction(isolationLevel);
  22:                 Transaction.Current = new CommittableTransaction(dbTransaction);
  23:             }
  24:             else
  25:             {
  26:                 Transaction.Current = transaction.DependentClone();
  27:             }           
  28:         }
  29:  
  30:         public void Complete()
  31:         {
  32:             this.Completed = true;
  33:         }
  34:         public void Dispose()
  35:         {
  36:             Transaction current = Transaction.Current;
  37:             Transaction.Current = transaction;
  38:             if (!this.Completed)
  39:             {
  40:                 current.Rollback();
  41:             }
  42:             CommittableTransaction committableTransaction = current as CommittableTransaction;
  43:             if (null != committableTransaction)
  44:             {
  45:                 if (this.Completed)
  46:                 {
  47:                     committableTransaction.Commit();
  48:                 }
  49:                 committableTransaction.Dispose();
  50:             }
  51:         }
  52:     }
  53: }

在事务操作完成之后必须调用Complete方法“提交事务”,此时我们将TransactionScope的Completed 属性设置为True。TransactionScope实现了IDisposable方法,Dispose方法具有这样的逻辑:先将当前状态还原成创建TransactionScope之初的状态,在还原之前先将当前事务保存下来。如果Completed属性为False,则直接调用Transaction的Rollback方法对事务进行回滚。如果Completed状态为True,而且当前事务为CommittableTransaction 则直接提交事务。

四、一个具有事务感知的DbHelper

为了演示通过捕捉当前事务来来控制具体事务的执行方式,我们写了如下一个DbHelper,其中ExecuteNonQuery用于在事务中执行指定的一段SQL。具体事务控制的逻辑是这样的:如果Artech.Transactions.Transaction.Current属性返回的事务存在,则将当前操作纳入封装的DbTransaction;如果System.Transactions.Transaction.Current属性返回的事务存在,操作的执行会自动纳入该事务中;如果上述两中环境事务均不存在,则创建一个单独的DbTransaction并将相应的操作纳入其中。

   1: public class DbHelper
   2: {
   3:     //其他成员
   4:     public int ExecuteNonQuery(string commandText, IDictionary<string, object> parameters)
   5:     {
   6:         DbConnection connection = null;
   7:         DbCommand command = this.DbProviderFactory.CreateCommand();
   8:         DbTransaction dbTransaction = null;
   9:         try
  10:         {
  11:             command.CommandText = commandText;
  12:             parameters = parameters ?? new Dictionary<string, object>();
  13:             foreach (var item in parameters)
  14:             {
  15:                 command.Parameters.Add(this.BuildDbParameter(item.Key, item.Value));
  16:             }
  17:             if (null != Artech.Transactions.Transaction.Current)
  18:             {
  19:                 command.Connection = Artech.Transactions.Transaction.Current.DbTransactionWrapper.DbTransaction.Connection;
  20:                 command.Transaction = Artech.Transactions.Transaction.Current.DbTransactionWrapper.DbTransaction;
  21:             }
  22:             else
  23:             {
  24:                 connection = this.DbProviderFactory.CreateConnection();
  25:                 connection.ConnectionString = this.ConnectionString;
  26:                 command.Connection = connection;
  27:                 connection.Open();
  28:                 if (System.Transactions.Transaction.Current == null)
  29:                 {
  30:                     dbTransaction = connection.BeginTransaction();
  31:                     command.Transaction = dbTransaction;
  32:                 }
  33:             }
  34:             int result = command.ExecuteNonQuery();
  35:             if (null != dbTransaction)
  36:             {
  37:                 dbTransaction.Commit();
  38:             }
  39:             return result;
  40:         }
  41:         catch
  42:         {
  43:             if (null != dbTransaction)
  44:             {
  45:                 dbTransaction.Rollback();
  46:             }
  47:             throw;
  48:         }
  49:         finally
  50:         {
  51:             if (null != connection)
  52:             {
  53:                 connection.Dispose();
  54:             }
  55:             if (null != dbTransaction)
  56:             {
  57:                 dbTransaction.Dispose();
  58:             }
  59:             command.Dispose();
  60:         }
  61:     }
  62: }

五、三种事务控制的性能

我们现在来测试批量操作在System.Transactions.TransactionScope、Artech.Transactions.TransactionScope和针对单独操作的DbTransaction的性能。我们在目标数据库中创建一个只包含Id和Name两个字段的数据表Users,并通过如下的CreateUser为该表添加一笔记录。

   1: static void CreateUser(string id, string name)
   2: {
   3:     string sql = "INSERT Users(ID, Name) Values(@id, @name)";
   4:     Dictionary<string, object> parameters = new Dictionary<string, object>();
   5:     parameters.Add("id", id);
   6:     parameters.Add("name", name);
   7:     DbHelper.ExecuteNonQuery(sql, parameters);
   8: }

而如下三个方法分别通过上述三种事务方式执行上面的这个方法,其中参数count为添加的数据量。

   1: private static void AddUsers1(int count)
   2: {
   3:     using (System.Transactions.TransactionScope transactionScope = new System.Transactions.TransactionScope())
   4:     {
   5:         for (int i = 0; i < count; i++)
   6:         {
   7:             CreateUser(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
   8:         }
   9:         transactionScope.Complete();
  10:     }
  11: }
  12:  
  13: private static void AddUsers2(int count)
  14: {
  15:     using (Artech.Transactions.TransactionScope transactionScope = new Artech.Transactions.TransactionScope("TestDb"))
  16:     {
  17:         for (int i = 0; i < count; i++)
  18:         {
  19:             CreateUser(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
  20:         }
  21:         transactionScope.Complete();
  22:     }
  23: }
  24:  
  25: private static void AddUsers3(int count)
  26: {
  27:     for (int i = 0; i < count; i++)
  28:     {
  29:         CreateUser(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
  30:     }
  31: }

为了剔除由于数据表现有数据量对数据访问性能的影响,我们在每次进行数据批量插入之前都回调用具有如下定义的DeleteAllUsers方法将表中的数据删除殆尽。

   1: private static void DeleteAllUsers()
   2: {
   3:     string sql = "DELETE USERS";
   4:     DbHelper.ExecuteNonQuery(sql, null);
   5: }

下面是我们最终的测试程序,我们分别测试了数据量为100、1,000、10,000和100,000的数据写入操作分别在上述三种事务控制方式的耗时。

   1: Stopwatch stopWatch = new Stopwatch();
   2:  
   3: //100
   4: Console.WriteLine("100");
   5: DeleteAllUsers();
   6: stopWatch.Restart();
   7: AddUsers1(100);
   8: Console.WriteLine("{0, -36}: {1}", "System.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
   9:  
  10: DeleteAllUsers();
  11: stopWatch.Restart();
  12: AddUsers2(100);
  13: Console.WriteLine("{0, -36}: {1}", "Artech.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
  14:  
  15: DeleteAllUsers();
  16: stopWatch.Restart();
  17: AddUsers3(100);
  18: Console.WriteLine("{0, -36}: {1}\n", "Single Transnaction", stopWatch.ElapsedMilliseconds);
  19:  
  20: //1000
  21: Console.WriteLine("1000");
  22: DeleteAllUsers();
  23: stopWatch.Restart();
  24: AddUsers1(1000);
  25: Console.WriteLine("{0, -36}: {1}", "System.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
  26:  
  27: DeleteAllUsers();
  28: stopWatch.Restart();
  29: AddUsers2(1000);
  30: Console.WriteLine("{0, -36}: {1}", "Artech.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
  31:  
  32: DeleteAllUsers();
  33: stopWatch.Restart();
  34: AddUsers3(1000);
  35: Console.WriteLine("{0, -36}: {1}\n", "Single Transnaction", stopWatch.ElapsedMilliseconds);
  36:  
  37: //10000
  38: Console.WriteLine("10000");
  39: DeleteAllUsers();
  40: stopWatch.Restart();
  41: AddUsers1(10000);
  42: Console.WriteLine("{0, -36}: {1}", "System.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
  43:  
  44: DeleteAllUsers();
  45: stopWatch.Restart();
  46: AddUsers2(10000);
  47: Console.WriteLine("{0, -36}: {1}", "Artech.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
  48:  
  49: DeleteAllUsers();
  50: stopWatch.Restart();
  51: AddUsers3(10000);
  52: Console.WriteLine("{0, -36}: {1}\n", "Single Transnaction", stopWatch.ElapsedMilliseconds);
  53:  
  54: //100000
  55: Console.WriteLine("100000");
  56: DeleteAllUsers();
  57: stopWatch.Restart();
  58: AddUsers1(100000);
  59: Console.WriteLine("{0, -36}: {1}", "System.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
  60:  
  61: DeleteAllUsers();
  62: stopWatch.Restart();
  63: AddUsers2(100000);
  64: Console.WriteLine("{0, -36}: {1}", "Artech.Transactions.TransactionScope", stopWatch.ElapsedMilliseconds);
  65:  
  66: DeleteAllUsers();
  67: stopWatch.Restart();
  68: AddUsers3(100000);
  69: Console.WriteLine("{0, -36}: {1}", "Single Transaction", stopWatch.ElapsedMilliseconds);

下面是测试程序输出结果:

   1: 100
   2: System.Transactions.TransactionScope: 28
   3: Artech.Transactions.TransactionScope: 11
   4: Single Transnaction                 : 57
   5:  
   6: 1000
   7: System.Transactions.TransactionScope: 140
   8: Artech.Transactions.TransactionScope: 83
   9: Single Transnaction                 : 469
  10:  
  11: 10000
  12: System.Transactions.TransactionScope: 1530
  13: Artech.Transactions.TransactionScope: 826
  14: Single Transnaction                 : 4659
  15:  
  16: 100000
  17: System.Transactions.TransactionScope: 13562
  18: Artech.Transactions.TransactionScope: 8346
  19: Single Transaction                  : 48277

上面的输出结果表明我们自定义的TransactionScope具有最好的性能,不过优势还不是很明显,这是由于连接池机制的原因。现在我们通过如下的方式将连接字符创对连接池的支持关闭:

   1: <configuration>
   2:   <connectionStrings>
   3:     <add name="TestDb" 
   4:          providerName="System.Data.SqlClient" 
   5:          connectionString="Data Source=.;Initial Catalog=TestDb;Integrated Security=True; Pooling=False;"/>
   6:   </connectionStrings>
   7: </configuration>

再次运行我们的程序我们就会看到三种事务处理方式在性能上的显著差异(当数据量为100,000时,采用System.Transactions.TransactionScope直接就崩溃了)

   1: 100
   2: System.Transactions.TransactionScope: 2318
   3: Artech.Transactions.TransactionScope: 13
   4: Single Transnaction                 : 310
   5:  
   6: 1000
   7: System.Transactions.TransactionScope: 2949
   8: Artech.Transactions.TransactionScope: 124
   9: Single Transnaction                 : 3623
  10:  
  11: 10000
  12: System.Transactions.TransactionScope: 32754
  13: Artech.Transactions.TransactionScope: 1213
  14: Single Transnaction                 : 30631
  15:  
  16: 100000
  17:  
  18: 未经处理的异常:  System.Data.SqlClient.SqlException: Import of Microsoft Distributed Transaction Coordinator (MS DTC) transaction failed:...
posted @ 2012-01-05 17:51 Artech 阅读(1816) 评论(30) 编辑 收藏

 回复 引用 查看   
#1楼[楼主] 2012-01-05 17:55 Artech      
 回复 引用 查看   
#2楼 2012-01-05 18:02 遗忘海岸      
收藏了
 回复 引用 查看   
#3楼 2012-01-05 19:37 南京.王清培      
好文,蒋先生辛苦了。书啥时候出版啊。我都急死了
 回复 引用 查看   
#4楼 2012-01-05 21:25 artwl      
占位,收藏细看,顶
 回复 引用 查看   
#5楼[楼主] 2012-01-05 21:42 Artech      
引用南京.王清培:好文,蒋先生辛苦了。书啥时候出版啊。我都急死了

我的工作大部分完成了,看出版社了。

 回复 引用 查看   
#6楼 2012-01-05 21:59 温景良(Jason)      
哈哈,刚想问书什么时候出版,结果上面看到答案了,出版了我一定买.
 回复 引用 查看   
#7楼 2012-01-05 22:47 lcs-帅      
这个dbheper有没有完整的呀。我想把我的dbhelper整个给换了。。。
 回复 引用 查看   
#8楼[楼主] 2012-01-05 23:01 Artech      
引用lcs-帅:这个dbheper有没有完整的呀。我想把我的dbhelper整个给换了。。。

这只是个简单的模拟程序而是,仅仅是对ADO.NET的简单封装而已。

 回复 引用 查看   
#9楼[楼主] 2012-01-05 23:01 Artech      
引用温景良(Jason):哈哈,刚想问书什么时候出版,结果上面看到答案了,出版了我一定买.

:)

 回复 引用 查看   
#10楼 2012-01-06 09:31 NqIceCoffee      
刚开始看到用static来传值,感觉肯定会有问题,当查到了ThreadStatic这个标记时,我恍然大悟,谢谢LZ,受教了~~~
 回复 引用 查看   
#11楼[楼主] 2012-01-06 09:39 Artech      
引用NqIceCoffee:刚开始看到用static来传值,感觉肯定会有问题,当查到了ThreadStatic这个标记时,我恍然大悟,谢谢LZ,受教了~~~

:)

 回复 引用 查看   
#12楼 2012-01-06 09:49 Jeffrey.Liang      
"我们经常会遇到这样的事务场景:当某个方法单独执行的时候,它应该在一个单一的DbTransaction中执行;当多个方法一起执行的时候,它们应用共同在DbTransaction中执行"

不知所云?

 回复 引用 查看   
#13楼 2012-01-06 09:57 NqIceCoffee      
12楼的领会精神撒,就是在多个类的多个方法中共用一个DbTransaction
 回复 引用 查看   
#14楼 2012-01-06 14:47 Kevin Zou      
收藏起来,以后有时间再来详读
 回复 引用   
#15楼 2012-01-06 15:00 fredli[未注册用户]
單一資料庫可行, 但若是多個資料庫連線交互的話就會有點問題
實務上常常method外部和內部可能會是主機相同,但資料庫不同的資料來源
若不考慮直接於sql裡面寫明dbname.dbo.tableName, 這會有辦法解決嗎?
Ex:
//DB1和DB2是同一個主機不同資料庫
static DbHelper db1 = new DbHelper("DB1");
static DbHelper db2 = new DbHelper("DB2");
static void Main(string[] args) {
using (Artech.Transactions.TransactionScope transactionScope = new Artech.Transactions.TransactionScope("DB1")) {
db1.ExecuteNonQuery("INSERT Users1(ID, Name) Values(@id, @name)", new Dictionary<string, object> {
{"id","a1"},
{"name","a2"},
});
db2.ExecuteNonQuery("INSERT Users2(ID, Name) Values(@id, @name)", new Dictionary<string, object> {
{"id","b1"},
{"name","b2"},
});
transactionScope.Complete();
}
}

 回复 引用 查看   
#16楼 2012-01-06 15:26 Daniel Cai      
引用fredli:
單一資料庫可行, 但若是多個資料庫連線交互的話就會有點問題
實務上常常method外部和內部可能會是主機相同,但資料庫不同的資料來源
若不考慮直接於sql裡面寫明dbname.dbo.tableName, 這會有辦法解決嗎?
Ex:
//DB1和DB2是同一個主機不同資料庫
static DbHelper db1 = new DbHelper("DB1");
static DbHelper db2 = new DbHelper("DB2");
static void Main(string[] args) {
using (Artech...


using (Artech.Transactions.TransactionScope transactionScope = new Artech.Transactions.TransactionScope("DB1"),transactionScope = new Artech.Transactions.TransactionScope("DB2"))
{...

 回复 引用 查看   
#17楼[楼主] 2012-01-06 15:30 Artech      
引用fredli:
單一資料庫可行, 但若是多個資料庫連線交互的話就會有點問題
實務上常常method外部和內部可能會是主機相同,但資料庫不同的資料來源
若不考慮直接於sql裡面寫明dbname.dbo.tableName, 這會有辦法解決嗎?
Ex:
//DB1和DB2是同一個主機不同資料庫
static DbHelper db1 = new DbHelper("DB1");
static DbHelper db2 = new DbHelper("DB2");
static void Main(string[] args) {
using (Artech...

我们采用的是DbTrasaction,自然只能支持Local Transaction,而不支持Distributed Transaction!!

 回复 引用   
#18楼 2012-01-07 11:05 xulon_7[未注册用户]
很喜欢看你写的文章。
wcf系列里,能写一下如何防范攻击吗?

 回复 引用 查看   
#19楼 2012-01-08 09:03 深海沉      
这个真的很好,有两个问题想请教一下:
1.系统的transactionscope是分布式的,那么这个是否也支持分布式?
2.系统的transactionscope不仅仅是针对Dbtransaction,有可能还有其它种事务,是否支持?

老A研究的很细致,很实用,支持

 回复 引用 查看   
#20楼[楼主] 2012-01-08 12:09 Artech      
引用深海沉:
这个真的很好,有两个问题想请教一下:
1.系统的transactionscope是分布式的,那么这个是否也支持分布式?
2.系统的transactionscope不仅仅是针对Dbtransaction,有可能还有其它种事务,是否支持?

老A研究的很细致,很实用,支持

Dbtransaction是基于DbConnection的,自然不能支持Distributed Transaction!

 回复 引用 查看   
#21楼 2012-01-11 11:14 非空      
MSDN:SQL Server 的 .NET Framework 提供程序支持可提升事务,这种事务通过 .NET Framework System.Transactions 命名空间中的类处理。 可提升事务通过将分布式事务推迟到需要时再创建,对分布式事务进行优化。如果只需要一个资源管理器,则不会发生任何分布式事务。
按照这种说法TransactionScope不应该如此不济啊(是不是DbTransaction有数据库支持呢?),
还有第二种是所有count都在一个事务中,而第三种循环一次就开启一个事物(是要测试连接池?)

 回复 引用 查看   
#22楼[楼主] 2012-01-11 12:45 Artech      
引用非空:
MSDN:SQL Server 的 .NET Framework 提供程序支持可提升事务,这种事务通过 .NET Framework System.Transactions 命名空间中的类处理。 可提升事务通过将分布式事务推迟到需要时再创建,对分布式事务进行优化。如果只需要一个资源管理器,则不会发生任何分布式事务。
按照这种说法TransactionScope不应该如此不济啊(是不是DbTransaction有数据库支持呢?),
还有第二种是所有count都在一个事务中,而第三种循环一次就开启一个事物(是要测试连接池?)

经过我的测试,在不支持连接池的情况下会自动提升到Distributed Transaction,反之则不会。

 回复 引用 查看   
#23楼 2012-01-18 12:54 笑看人间      
非常不错,不过你的方式太局限。只能局部性控制,无法做到嵌套性使用,从而导致在某些方面还是不能做到事务的完整性提交,例如A类有此写法、B类有个方法也是按这种风格编写,那在编写中A和B只考虑保证各自当前的事务的处理,但如果A类需要调用B类呢?可否把A和B的事务结合成一个事务类,所谓的事务串联。不知道是我没有看懂上面的例子理解你的思路还是?呵呵,我写过一很非常不错的例子,支持无限层的嵌套,在编写代码时只需要关注我们的业务不需要去关注事务的处理,当然必须要按照一个统一的规范去写。此机制目前支撑一个类似银行系统体系的项目,大家可以互相指教,写出更好的程序来,呵呵....
 回复 引用 查看   
#24楼[楼主] 2012-01-18 13:26 Artech      
引用笑看人间:非常不错,不过你的方式太局限。只能局部性控制,无法做到嵌套性使用,从而导致在某些方面还是不能做到事务的完整性提交,例如A类有此写法、B类有个方法也是按这种风格编写,那在编写中A和B只考虑保证各自当前的事务的处理,但如果A类需要调用B类呢?可否把A和B的事务结合成一个事务类,所谓的事务串联。不知道是我没有看懂上面的例子理解你的思路还是?呵呵,我写过一很非常不错的例子,支持无限层的嵌套,在编写代码时只需要关注我们的业务不需要去关注事务的处理,当然必须要按照一个统一的规范去写。此机制目前支撑一个类似银行系统体系的项目,大家可以互相指教,写出更好的程序来,呵呵....

你为什么说我的TransactionScope不支持嵌套呢?

 回复 引用 查看   
#25楼 2012-01-18 23:31 Eric.rz.Liang      
牛~

我们的代码还需要SetTransaction(tran)...

public int AddModel(AppMenuModel model)
        {
            CheckModel(model, true);
            using (DbTransaction tran = DataAccess.InitDB.BeginTransaction())
            {
                model.UpdateDate = System.DateTime.Now;
                if (model.ParentId > 0)
                    model.GroupId = GetGroupId(model.ParentId, tran);
                AppMenuTable table = new AppMenuTable();
                model.Id = DataAccess.InitDB.Insert(table)
                    .SetTransaction(tran)
                    .AddColumn(table.Title, model.Title)
                    .AddColumn(table.Url, model.Url)
                    .AddColumn(table.Target, model.Target)
                    .AddColumn(table.VisibleIndex, model.VisibleIndex)
                    .AddColumn(table.Icon, model.Icon)
                    .AddColumn(table.ParentId, model.ParentId)
                    .AddColumn(table.GroupId, model.GroupId)
                    .AddColumn(table.ModuleId, model.ModuleId)
                    .AddColumn(table.Creatable, model.Creatable)
                    .AddColumn(table.Active, model.Active)
                    .AddColumn(table.UpdateUser, model.UpdateUser)
                    .AddColumn(table.UpdateDate, model.UpdateDate)
                    .ExecuteIdentity(table.Id);
                AppRoleMenuTable roleMenu = new AppRoleMenuTable();
                foreach (int r in model.Role)
                {
                    DataAccess.InitDB.Insert(roleMenu)
                        .SetTransaction(tran)
                        .AddColumn(roleMenu.RoleId, r)
                        .AddColumn(roleMenu.MenuId, model.Id)
                        .Execute();
                }
                tran.Commit();
                return model.Id;
            }
        }

 回复 引用 查看   
#26楼 2012-02-08 14:43 imap      
发现一个问题,微软的TransactionScope有如下构造函数:
public TransactionScope(TransactionScopeOption scopeOption, TransactionOptions transactionOptions); 其中TransactionOptions可以设置事务超时时间,你的自定义TransactionScope类如何设置事务超时时间啊,望楼主赐教,感谢。

 回复 引用 查看   
#27楼[楼主] 2012-02-08 14:54 Artech      
引用imap:
发现一个问题,微软的TransactionScope有如下构造函数:
public TransactionScope(TransactionScopeOption scopeOption, TransactionOptions transactionOptions); 其中TransactionOptions可以设置事务超时时间,你的自定义TransactionScope类如何设置事务超时时间啊,望楼主赐教,感谢。

DbTransaction没有Timeout,一般我们设置DbCommmand的CommandTimeout属性!

 回复 引用 查看   
#28楼 2012-02-08 22:36 imap      
@Artech
哦,多谢。

 回复 引用 查看   
#29楼 2012-02-19 22:41 show_show      

我现在用到项目中了,。。非常顺手。。

网上很多人都在讨论 传统三层的业务逻辑层的作用。。其实 这个不就很好的给出了答案吗? 事务处理就可以移到 业务层。。

数据库层专注处理 数据的 增删改查。。。

至少对大部分的企业项目来说。。这个 非常不错!

 回复 引用 查看   
#30楼 2012-02-21 09:48 自由骑士      
@show_show
事务处理就可以移到业务层,也需要像Artech这个实例这样在DBHelper里感知事物吧,能吧你的dbhelper拿出来让我观摩一下吗?

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 2313490 5eJKb/3h7JU=