zoukankan      html  css  js  c++  java
  • 关于使用Transaction对于非数据库事务的操作

    在操作数据库的过程中,为了数据的一致性,我们可以使用Transaction,要么成功的时候全部提交,要么有任何一个操作失败立即全部回滚。不仅仅是在数据库方面,有时候操作其他的内容,比如说对于系统文件的操作,也需要把一些操作组合看做是一个事务。

    现在我们看这样一个例子。现在我们需要在计算机的硬盘上创建3个目录A,B,C,要求要么3个全部创建成功,要么一个也不要创建。我们可以把这个看成是一个事务。如果我们自己写代码来操作,可以这样写。

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. bool createA = false;  
    2. bool createB = false;  
    3. bool createC = false;  
    4.   
    5. try  
    6. {  
    7.     //这里的操作是创建3个目录  
    8.     Directory.CreateDirectory("\A");  
    9.     createA = true;  
    10.     Directory.CreateDirectory("\B");  
    11.     createB = true;  
    12.     Directory.CreateDirectory("\C");  
    13.     createC = true;  
    14. }  
    15. catch (System.Exception ex)  
    16. {  
    17.     //这里在捕捉到异常时,根据运行的结果进行回滚  
    18.     if (createB)  
    19.     {  
    20.         Directory.Delete("\B");  
    21.         Directory.Delete("\A");  
    22.     }  
    23.     if (!createB && createA)  
    24.     {  
    25.         Directory.Delete("\A");  
    26.     }  
    27. }  



    但是这里我们是把这3个操作当成一个整体来回滚,及时是简单的创建删除文件夹,我们可以看到catch中的回滚逻辑已经很复杂了。可以想象,如果A,B,C这3步不仅仅是创建目录,还有一些其他的操作,那么回滚的逻辑就非常复杂,此时,我们可以考虑把这一个分成几个小事务,分开回滚。代码可以这样写,这里我们使用了Framework提供的Transactin以及TransactioScope类。

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. class Program  
    2.     {  
    3.         static void Main(string[] args)  
    4.         {  
    5.             //这里就不需要了try catch,因为scope就已经完成了这个功能,  
    6.             //即当有异常发生向外抛的时候,会尝试跳出这个using代码块,  
    7.             //CLR会在向代码块外边跑异常之前,分别取调用每个transaction的  
    8.             //roolback方法,只要rollback中你定义的逻辑没有问题,那么所有  
    9.             //的已发生的操作就会安全的回滚。  
    10.             using (var scope = new TransactionScope())  
    11.             {  
    12.                 var A = new OperationA();  
    13.                 Transaction.Current.EnlistVolatile(A, EnlistmentOptions.None);  
    14.                 A.DoWork();  
    15.   
    16.                 OperationB B = new OperationB();  
    17.                 Transaction.Current.EnlistVolatile(B, EnlistmentOptions.None);  
    18.                 B.DoWork();  
    19.                 scope.Complete();  
    20.             }  
    21.         }  
    22.   
    23.   
    24.     }  
    25. }  
    26.   
    27. class OperationA : IEnlistmentNotification  
    28. {  
    29.     private bool _isCommitSucceed = false;  
    30.   
    31.     public void Commit(Enlistment enlistment)  
    32.     {  
    33.         enlistment.Done();  
    34.     }  
    35.   
    36.     public void DoWork()//这是自定义的方法,不是继承IEnlistmentNotification  
    37.     {  
    38.         Directory.CreateDirectory("\A");  
    39.         //这里还有一些其他的关于A的复杂操作  
    40.         _isCommitSucceed = true;  
    41.     }  
    42.   
    43.     public void InDoubt(Enlistment enlistment)  
    44.     {  
    45.         enlistment.Done();  
    46.     }  
    47.     public void Prepare(PreparingEnlistment preparingEnlistment)  
    48.     {  
    49.         preparingEnlistment.Prepared();  
    50.     }  
    51.     public void Rollback(Enlistment enlistment)  
    52.     {  
    53.         //这里回滚A的一些操作,当然这里的操作逻辑需要你自己来写。  
    54.         //比如说查看是否创建成功,或者有一些其他的信号标记,通过这些  
    55.         //标记你来决定是删除目录还是其他的什么回滚操作。  
    56.         if (_isCommitSucceed)  
    57.             Directory.Delete("\A");  
    58.         enlistment.Done();  
    59.     }  
    60. }  
    61.   
    62. class OperationB : IEnlistmentNotification  
    63. {  
    64.     private bool _isCommitSucceed = false;  
    65.   
    66.     public void Commit(Enlistment enlistment)  
    67.     {  
    68.         enlistment.Done();  
    69.     }  
    70.   
    71.     public void DoWork()  
    72.     {  
    73.         //这里是关于B的一些复杂操作  
    74.         throw new Exception("test");  
    75.         //这里依然有一些操作代码,但是我们模拟的是B操作途中抛出异常,所以这里的代码不会执行  
    76.     }  
    77.   
    78.     public void InDoubt(Enlistment enlistment)  
    79.     {  
    80.         enlistment.Done();  
    81.     }  
    82.     public void Prepare(PreparingEnlistment preparingEnlistment)  
    83.     {  
    84.         preparingEnlistment.Prepared();  
    85.     }  
    86.     public void Rollback(Enlistment enlistment)  
    87.     {  
    88.         if (_isCommitSucceed)  
    89.         {  
    90.             //这里回滚B的一些已经存在的操作。  
    91.         }  
    92.         enlistment.Done();  
    93.     }  


    这里的机制是这样的,可能是scope.Complete()方法中有某种特别的操作,去告诉CLR这次所有的操作都顺利完成了,在跳出这个scope的时候就不用调用那些transaction的rollback方法了。如果没有执行到scope.Complete()方法,那么就会在跳出这个scope代码块的时候调用rollback方法。一般造成这个的原因是在某个transaction的逻辑操作中出现异常,造成从此次直接抛出异常跳出这个代码块,没有机会执行下边的代码。可以看出这里的重点也是写rollback的逻辑,但是相对于原来的catch中的逻辑,这里分开为多个小的逻辑,相对来说容易了很多。

    这只是个人写的一种使用回滚的逻辑。在使用IEnlistmentNotification的时候,也有人把业务逻辑写入到Commit中。如果想真正理解transaction回滚的机制,建议深入理解一下TransactionScope与Transactiond的实现机制。

  • 相关阅读:
    41.js延迟加载的方式有哪些?
    39、[“1”, “2”, “3”].map(parseInt) 答案是多少
    38.null,undefined 的区别?
    35.说几条写JavaScript的基本规范?
    34.介绍js有哪些内置对象?
    问题解决Android studio遇到 java.lang.OutOfMemoryError: GC app:transformClassesWithDexForDebug解决方法 以及gradle优化
    Multiple dex files define
    Retrofit2.0+RxJava2.0问题
    【转】Android Shape绘制虚线在手机端查看是实线的问题
    极光使用整理
  • 原文地址:https://www.cnblogs.com/sky6699/p/4535760.html
Copyright © 2011-2022 走看看