像TransactionScope一样使用DbTransaction
System.Transactions.TransactionScope为了提供一种非常方便的实现分布式事务的方式,但是在某些情况下为了阻止本地事务向分布式事务提升,我们只能通过DbTransaction进行事务控制。在进行数据库操作的时候,我们经常会遇到这样的事务场景:当某个方法单独执行的时候,它应该在一个单一的DbTransaction中执行;当多个方法一起执行的时候,它们应用共同在DbTransaction中执行。在本篇文章中我们通过对DbTransaction进行封装,提供一种类似于TransactionScope的编程方式来解决这个问题。[源代码从这里下载]
一、自定义的Transaction我们完全采用System.Transactions的应用编程接口的设计定义基于DbTransaction的相关类型,首选来看看具有如下定义的表示事务的Transaction类型。Transaction是一个抽象类,具有DbTransactionWrapper和Completed两个属性,前者表示对DbTransaction的封装,后者表示事务操作是否已经完成。静态属性Current表示当前事务,这是一个基于当前线程的静态字段。Rollback和Dispose直接调用了DbTransactionWrapper的同名方法。 1: namespace Artech.Transactions 3: public class DbTransactionWrapper: IDisposable 5: public DbTransactionWrapper(DbTransaction transaction)
7: this.DbTransaction = transaction;
9: public DbTransaction DbTransaction { get; private set; } 11: void Rollback()
13: if (!this.IsRollBack) 15: this.DbTransaction.Rollback();
17: } 19: { 21: } 23: { 25: } 27: abstract class Transaction : IDisposable 29: [ThreadStatic] 31:? 33: public DbTransactionWrapper DbTransactionWrapper { get; protected set; } 35: 36: {
38: } 40: { 42: } 44: { 46: } 48: { 50: set { current = value; }
52: } public CommittableTransaction(DbTransaction dbTransaction)6: }8: {10: }class DependentTransaction : Transaction5: {7: this.DbTransaction = this.InnerTransaction.DbTransaction;9: } 三、自定义TransactionScope我们在进行事务编程的时候只会使用到具有如下定义的TransactionScope类型。我们通过指定连接字符串名称、隔离级别以及用于创建DbProviderFactory的委托创建TransactionScope对象。在TransactionScope的构造函数中,如果通过Artech.Transactions.Transaction.Current属性表示的当前事务不存在,则根据DbProviderFactory创建DbConnection并调用BeginTransaction方法开启事务,并用被开启的DbTransaction创建CommittableTransaction对象。最终将创建的CommittableTransaction作为当前事务;服务过当前事务已经存在,则直接调用它的DependentClone方法创建的DependentTransaction作为当前事务。 private Transaction transaction = Transaction.Current;
8: public TransactionScope(string connectionStringName,IsolationLevel isolationLevel = IsolationLevel.Unspecified, 10: { 12: { 14: { 16: } 18: DbConnection connection = factory.CreateConnection(); 20: connection.Open(); 22: Transaction.Current = new CommittableTransaction(dbTransaction);
else 26: Transaction.Current = transaction.DependentClone(); 28: } void Complete() 32: this.Completed = true; 35: { 37: Transaction.Current = transaction; 39: { 41: } 43: null != committableTransaction)
45: this.Completed)
47: committableTransaction.Commit(); 49: committableTransaction.Dispose(); 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并将相应的操作纳入其中。 //其他成员
6: DbConnection connection = null;
8: DbTransaction dbTransaction = try
12: parameters = parameters ?? new Dictionary<object>(); 15: command.Parameters.Add(this.BuildDbParameter(item.Key,item.Value));
18: { 20: command.Transaction = Artech.Transactions.Transaction.Current.DbTransactionWrapper.DbTransaction; 22: 23: {
25: connection.ConnectionString = this.ConnectionString;
27: connection.Open(); 29: { 31:???????????????????? command.Transaction = dbTransaction; 33: } 35: null != dbTransaction)
37: dbTransaction.Commit(); 39: return result;
41: catch
45: dbTransaction.Rollback(); 47: throw;
49: finally
51: null != connection)
53: connection.Dispose(); 55: 56: {
58: } 60: } 62: } 五、三种事务控制的性能我们现在来测试批量操作在System.Transactions.TransactionScope、Artech.Transactions.TransactionScope和针对单独操作的DbTransaction的性能。我们在目标数据库中创建一个只包含Id和Name两个字段的数据表Users,并通过如下的CreateUser为该表添加一笔记录。 string sql = "INSERT Users(ID,Name) Values(@id,@name)";
6: parameters.Add("name",name);
8: } 而如下三个方法分别通过上述三种事务方式执行上面的这个方法,其中参数count为添加的数据量。 using (System.Transactions.TransactionScope transactionScope = new System.Transactions.TransactionScope())
7: CreateUser(Guid.NewGuid().ToString(),Guid.NewGuid().ToString()); 11: } 13: void AddUsers2( 14: { 16: { 19: CreateUser(Guid.NewGuid().ToString(),1)" id="lnum20"> 20: } 22: } 24:? 29: CreateUser(Guid.NewGuid().ToString(),1)" id="lnum30"> 30: } void DeleteAllUsers() 4: DbHelper.ExecuteNonQuery(sql,1)">null); 1: Stopwatch stopWatch = new Stopwatch();
3: //100
5: DeleteAllUsers(); 7: AddUsers1(100); 9:? 11: stopWatch.Restart(); 13: Console.WriteLine("Artech.Transactions.TransactionScope",stopWatch.ElapsedMilliseconds);
15: DeleteAllUsers(); 17: AddUsers3(100); 20: //1000
22: DeleteAllUsers(); 24: AddUsers1(1000); 27: DeleteAllUsers(); 29: AddUsers2(1000); 33: stopWatch.Restart(); 35: Console.WriteLine( 36:?
38: Console.WriteLine("10000");
40: stopWatch.Restart(); 42: Console.WriteLine( 43:?
45: stopWatch.Restart(); 47: Console.WriteLine( 48:?
50: stopWatch.Restart(); 52: Console.WriteLine( 53:?
55: Console.WriteLine("100000");
57: stopWatch.Restart(); 59: Console.WriteLine( 60:?
62: stopWatch.Restart(); 64: Console.WriteLine( 65:?
67: stopWatch.Restart(); 69: Console.WriteLine("Single Transaction",stopWatch.ElapsedMilliseconds);
下面是测试程序输出结果: 2: System.Transactions.TransactionScope: 28
4: Single Transnaction : 57 6: 1000 8: Artech.Transactions.TransactionScope: 83 10:? 12: System.Transactions.TransactionScope: 1530 14: Single Transnaction : 4659 16: 100000 18: Artech.Transactions.TransactionScope: 8346 <configuration> add name="TestDb" 5: connectionString="Data Source=.;Initial Catalog=TestDb;Integrated Security=True; Pooling=False;"/> 2: System.Transactions.TransactionScope: 2318 4: Single Transnaction : 310 8: Artech.Transactions.TransactionScope: 124 12: System.Transactions.TransactionScope: 32754 14: Single Transnaction : 30631 18: 未经处理的异常: System.Data.SqlClient.SqlException: Import of Microsoft Distributed Transaction Coordinator (MS DTC) transaction failed:... (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net – 无法为自定义MembershipProvider创建Membership
- ef-code-first – 如何首先使用代码向Identity默认表AspNet
- 如何说服我的管理员从ASP.NET 2.0升级到3.5?
- asp.net – .NET 4.0 ObjectCache的线程安全和范围管理
- asp.net core WebAPI实现CRUD
- asp.net – 在我的浏览器中测试Accept语言
- asp.net-mvc-3 – Mvc 3图像上传库
- asp.net – 从Web应用程序编辑MS Office文档:自定义WebDaV
- asp.net-mvc – 如何将ASP.net身份角色放入Identityserver4
- asp.net – 如何扩展aspnet成员身份验证表?