加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

像TransactionScope一样使用DbTransaction

发布时间:2020-12-16 09:08:27 所属栏目:asp.Net 来源:网络整理
导读:System.Transactions.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
   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 : Transaction
   5:     {
   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:...

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读