• C#——细说事务(下)


    目录

    一、事务的定义

    二、事务管理器

    三、在ADO.NET中实现事务

    四、隐式事务 TransactionScope

    五、在WCF中实现事务

    六、嵌套式事务

    七、异步事务

    六、分布式隐式事务

    嵌套式事务经常会出现在项目中,但往往容易被大家忽略,下面介绍一下 嵌套式事务的用法:(该事例可以说明很多问题)

    使用分布式事务必须开启服务:Distributed Transaction Coordinator 否则报错  “服务器 'MRWANG' 上的 MSDTC 不可用

    //分布在不同数据库之间的事务操作
    using(Connection con1=new Connection("连接字符串1"))
    using (TransactionScope scope1 = new TransactionScope())            
     {
            //............scope1事务处理操作        ///--此处提交将和外层(scope1)TransactionScope无关--
           //在无事务环境中执行操作,取消环境事务(TransactionScopeOption.Suppress)
                //每次进入创建新的事务环境(TransactionScopeOption.RequiresNew)          

            ///--scope2提交将和外层(scope1)TransactionScope有密切关联scope2提交成功,外层遇到异常,则全部回滚,因为属于同一个事务环境--
            //每次进入将检测是否存在事务环境,若存在则使用,不存在则重新创建(TransactionScopeOption.Required)

          using(Connection con2=new Connection("连接字符串2"))
            using (TransactionScope scope2=new TransactionScope(TransactionScopeOption.Required))
            {
            //............scope2事务处理操作
           scope2.Complete(); //只完成嵌套式的内部事务,但事务并未正式提交
    } scope1.Complete(); //代表完成所有事务,事务正式提交
    }

    一 般项目中,大家都只会把事务用在DAL层,用于管理数据的CRUD,但其实在一些操作中,某些数据的操作必须具有一致性。比如在订单管理中,当插入一条 OrderItem时,Order表内的总体价格,商品数量等也会随之改变。很多人把两个表的操作合成一个方法,放在OrderDAL中完成。但其实这样 做违返设计的原则,因为计算Order的总体价格时可能会包含商品优惠、客户等级、客户积分等等业务逻辑,而在DAL层不应该包含任何的业务逻辑存在的, 所以这样操作应该放在业务层完成。这时候,业务层的方法内就需要同时调用OrderItemDAL的AddOrderItem(OrderItem) 方法和OrderDAL的UpdateOrder(Order)方法,为了保证数据的一致性更新,就需要使用嵌套式事务。但这往往容易被开发人员所忽略, 当Order表的更新成功而OrderItem表的插入失败时,系统不能保证数据的同步回滚,那就会造成数据的逻辑性错误。

    下面的例子就是为了保证数据一致性更新而使用的嵌套式事务,在使用嵌套式事务的时候要应该注意及其把对象释放,避免造成死锁

     1 namespace DAL
     2 {     
     3      public class OrderDAL
     4      {
     5          public void UpdateOrder(Order order)
     6          {
     7              using (TransactionScope scope = new TransactionScope())
     8              {
     9                   ......         
    10                   scope.Complete();
    11              }
    12          }
    13      }
    14  
    15      public class OrderItemDAL
    16      {
    17          public void AddOrderItem(OrderItem orderItem)
    18          {
    19              using (TransactionScope scope = new TransactionScope())
    20              {
    21                  ......
    22                  scope.Complete();
    23              }
    24          }
    25      }
    26  }
    27 
    28 namespace BLL
    29 {
    30      public class OrderManager
    31      {
    32          public void AddOrderItem(OrderItem item)
    33          {
    34              using (TransactionScope scope = new TransactionScope())
    35              {
    36                  OrderItemDAL orderItemDAL=new OrderItemDAL();
    37                  orderItemDAL.AddOrderItem(item);
    38                  OrderDAL orderDAL=new OrderDAL();
    39                  ........
    40                  orderDAL.UpdateOrder(order);
    41                  scope.Complete();
    42              }
    43          }
    44      }
    45 }

    回到目录

     

    七、异步事务

    记得在第二节的时候曾经提起过事务类Transaction的方法中包含方法

    public DependentTransaction DependentClone(DependentCloneOption)

    此方法作用是克隆当前的事务,它在多线程调用同一事务的情况下使用经常使用。其中DependentCloneOption包含有两个选项:

    一为BlockCommitUntilComplete,这表示在依赖事务未完成前,事务将处于阻塞状态,只有在所有依赖事务完成后,事务才能执行提交;

    二为RollbackInNotComplete,这表示依赖事务必须在事务完成前调用Complete(),否则事务会被视为失败。

    在 普通情况下,事务都会通过Transaction.Current 来获取,但此方法只能获取当前线程下的事务对象,在异步方法当中,这只会返回一个空值 null 。此时就需要使用DependentClone 方法获取依赖事务对象 DependentTransaction ,再把此对象作为参数传递到回调函数中。

     1      class Program
     2      {
     3          static void Main(string[] args)
     4          {
     5              Method();
     6              Console.ReadKey();
     7          }
     8  
     9          static void Method()
    10          {
    11              using (TransactionScope scope = new TransactionScope())
    12              {
    13                  ShowMessage("Main Thread");
    14  
    15                  //获取一个依赖事务,把依赖事务作为回调参数传到回调函数中
    16 DependentTransaction dependentTransaction= 17 Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete); 18 ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncThread), dependentTransaction); 19 ........ 20 scope.Complete(); //完成主线程事务,在依赖事务完成前,事务提交将处于阻塞状态 21 } 22 } 23 24 static void AsyncThread(object transaction) 25 { 26 //获取依赖事务,利用TransactionScope(Transaction)构造函数生成隐式事务
    27 DependentTransaction dependentTransaction = (DependentTransaction)transaction; 28 using (TransactionScope scope = new TransactionScope(dependentTransaction)) 29 { 30 ShowMessage("AsyncThread"); 31 .......... 32 scope.Complete(); //完成异步事务
    33 } 34 //完成依赖事务
    35 dependentTransaction.Complete(); 36 } 37 38 static void ShowMessage(string data) 39 { 40 if (Transaction.Current != null) 41 { 42 Transaction transaction = Transaction.Current; 43 string info = string.Format("{0}:{1}\nTransaction:\n DistributedIndentifier:{2} \n LocalIndentifier:{3}\n", 44 data,Thread.CurrentThread.ManagedThreadId.ToString(), 45 transaction.TransactionInformation.DistributedIdentifier, 46 transaction.TransactionInformation.LocalIdentifier); 47 Console.WriteLine(info); 48 } 49 } 50 }

    首 先在主线程中利用 Transaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete) 方法生成一个依赖事务,注意方法使用了BlockCommitUntilComplete的方式生成,即事务将在所有依赖事务使用Complete()后 才能执行提交。

    然后利用ThreadPool.QueueUserWorkItem(WaitCallback,Object)方法把依赖事务作为回调参数传递到回调函数中。

    最后在回调函数中使用TransactionScope(transaction)构造函数生成对象,这代表把参数transaction作为当前的环境事务对象。观察下面的运行结果,两个线程中的事务都是同一个事务。

    结束语

    事务是在多个层次都会使用到的,但很多项目当中往往会忽略了这一点而只在数据层使用,在大型的系统当中这样可能会影响到系统的一致性。特别是在分布式系统当中,操作往往同时存在于多个不同的系统当中,事务的处理更显示出其重要性。

  • 相关阅读:
    AcWing 276. I-区域
    学习笔记:可持久化线段树(主席树):静态 + 动态
    NOIP2016提高组 天天爱跑步
    AcWing 195. 骑士精神
    标准文档流
    css 盒模型
    css 继承性和层叠性
    css 选择器
    css 引入方式
    html body中的标签2
  • 原文地址:https://www.cnblogs.com/wang726zq/p/Transaction1.html
Copyright © 2020-2023  润新知