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的实现机制。

  • 相关阅读:
    入门菜鸟
    FZU 1202
    XMU 1246
    Codeforces 294E Shaass the Great 树形dp
    Codeforces 773D Perishable Roads 最短路 (看题解)
    Codeforces 814E An unavoidable detour for home dp
    Codeforces 567E President and Roads 最短路 + tarjan求桥
    Codeforces 567F Mausoleum dp
    Codeforces 908G New Year and Original Order 数位dp
    Codeforces 813D Two Melodies dp
  • 原文地址:https://www.cnblogs.com/sky6699/p/4535760.html
Copyright © 2011-2022 走看看