好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

.NET可逆框架设计

.NET可逆框架设计

前段时间一直在学习和研究.NET事务处理,慢慢的我发现可以使用事务处理来实现一种可逆的系统框架。这种框架在一些IT社区似乎还没有见过,但是在我们日常开发中确实有这个需求。所以我花了点时间深入的研究了一下事务的原理和使用,实现了以事务为纽带,以资源为操作对象的可逆框架。

这里我假设您对事务有了整体的认识,也对自定义事务管理器有过了解。 [王清培版权所有,转载请给出署名]

(可以参考本人的: .NET 简谈事务本质论 、 .NET 简谈自定义事务资源管理器 )

1.    什么是可逆的程序框架

什么叫可逆的?程序的执行是可以被无限制回滚的。

什么叫可逆的框架?实现了对可逆功能的封装,并能通过简单的接口调用进行使用。框架可能有大有小,我想这么称呼它是为了表达它的整体性和重要性。

那么到底可逆的需求在哪里?其实在我们开发程序的时候经常会使用事务来进行业务的控制。比如删除订单,然后删除订单明细等等,对于这样的要求很多,我们只能将逻辑控制在一个事务范围内,不能在没有事务性的逻辑代码中编写这种要求的业务功能。等出现未知错误的时候在进行事务的回滚。

你也许会问,使用原来的事务处理不是也能进行回滚吗?当然不是这么简单的,我们使用事务回滚时只能将资源回滚到最初未进行事务处理前的状态。(这里不仅仅指的是数据库事务,而是全局的事务处理) 我们用图做个比较。 [王清培版权所有,转载请给出署名]

传统的事务处理图:

可逆的事务处理图:

从这两幅图中我们可以很明显的看出,传统的事务处理在事务处理的过程当中无法控制中间数据,也就是说无法对事务处理进行分段,然后在进行统一的提交或回滚。

在可逆框架的事务处理里我们就可以控制事务的执行阶段,在必要的时候我们只需提交或者回滚某一阶段的数据。

1.1 环境事务

在可逆框架的事务处理图中,我们看到事务的开始,然后就进行下一步、下一步这样的操作。在每进行一个下一步操作的时候,就是进入到了一个子事务里处理,在.NET中是可以进行事务的嵌套,其实也就是依赖事务Dependent Transaction实现。通过使用环境事务可以让事务性感知代码能自动的识别出您将要使用事务进行操作。所以在每进行下一步操作的时候,只有将当前环境事务切换为您将依赖的子事务才行。如果只是单纯的使用依赖事务对象实例在使用,那么将无法进行诸多其他的事务处理。

2可逆框架的实现原理

由于我们只能控制自定义事务资源管理器的内部实现,所以我们在构建自己的数据处理时问题变的简单多了。

实现可逆框架的核心技术就是使用依赖事务进行事务的克隆操作。将一个大的事务处理逻辑上切割成多了小的事务操作,然后在进行统一的提交或回滚。

在实现上其实就是将Committable Transaction对象进行包装,实现简单的调用接口。这里参照了环境代码的概念,将对象的生命周期控制在代码片段中。

2.1 自定义资源管理器的实现

我们需要扩展IEnlistmentNotification接口的实现,加入对“上一步”、“下一步”的数据操作。

请看代码:

View Code

  1   /*  **
   2    * author:深度训练
   3    * blog:  http://wangqingpei557.blog.51cto.com/ 
  4    * *  */ 
  5   using   System;
   6   using   System.Collections.Generic;
   7   using   System.Text;
   8   using   System.Transactions;
   9  
 10   namespace   ReversibleLib
  11   {
  12       ///   <summary> 
 13       ///   可逆范围内的资源管理器。
  14       ///   可以使用该类对易失性资源进行事务范围内的管理。在事务操作范围内进行可逆操作。
  15       ///   </summary> 
 16       ///   <typeparam name="T">  需要管理的资源类型  </typeparam> 
 17       ///   <typeparam name="Xcopy">  资源在使用、恢复过程中的数据复制对象。  </typeparam> 
 18       public   class  ReResourceManager<T, Xcopy> : IEnlistmentNotification, IReversibleGetResourceData<T>
 19           where  T :  class ,  new  ()
  20           where  Xcopy :  class 
 21       {
  22           ///   <summary> 
 23           ///   私有字段。资源的持久引用。
  24           ///   </summary> 
 25           T _commitfrontvalue;
  26           ///   <summary> 
 27           ///   私有字段。事务性操作数据对象。
  28           ///   </summary> 
 29          T _rollbackfrontvalue =  new   T();
  30           ///   <summary> 
 31           ///   保存数据复制对象。
  32           ///   </summary> 
 33           Xcopy _copy;
  34           ///   <summary> 
 35           ///   泛型约束需要,内部使用。
  36           ///   </summary> 
 37           public   ReResourceManager() { }
  38           ///   <summary> 
 39           ///   资源管理器内部名称。便于追踪
  40           ///   </summary> 
 41           public   string  Name {  get ;  set  ; }
  42           ///   <summary> 
 43           ///   重载默认构造函数,使用资源类型和数据复制对象初始化资源管理器。
  44           ///   </summary> 
 45           public   ReResourceManager(T t, Xcopy icopy)
  46           {
  47              (icopy  as  IResourceCopy<T> ).Copy(_rollbackfrontvalue, t);
  48              _commitfrontvalue =  t;
  49              _copy =  icopy;
  50           }
  51  
 52           #region  IEnlistmentNotification 成员
 53           public   void   Prepare(PreparingEnlistment preparingEnlistment)
  54           {
  55               preparingEnlistment.Prepared();
  56           }
  57           public   void   Commit(Enlistment enlistment)
  58           {
  59               enlistment.Done();
  60           }
  61           public   void   InDoubt(Enlistment enlistment)
  62           {
  63               enlistment.Done();
  64           }
  65           public   void   Rollback(Enlistment enlistment)
  66           {
  67              (_copy  as  IResourceCopy<T>).Copy(_commitfrontvalue, _rollbackfrontvalue); //  回滚事务 
 68               enlistment.Done();
  69           }
  70           #endregion 
 71  
 72           #region  IReversibleGetResourceData<T> 成员
 73          T IReversibleGetResourceData<T> .GetPreviousData()
  74           {
  75              T result =  new   T();
  76              (_copy  as  IResourceCopy<T> ).Copy(result, _rollbackfrontvalue);
  77               return   result;
  78           }
  79          T IReversibleGetResourceData<T> .GetNextData()
  80           {
  81              T result =  new   T();
  82              (_copy  as  IResourceCopy<T> ).Copy(result, _commitfrontvalue);
  83               return   result;
  84           }
  85           #endregion 
 86       }
  87  }

2.2 可逆框架的入口实现

我们需要简单的调用就能方便的使用可逆功能,不能以一种新的方式使用。所以这里借鉴了Transaction Scope的设计思想。

请看代码:

View Code

 1   /*  **
    2    * author:深度训练
    3    * blog:  http://wangqingpei557.blog.51cto.com/ 
   4    * *  */ 
   5   using   System;
    6   using   System.Collections.Generic;
    7   using   System.Text;
    8   using   System.Transactions;
    9  
  10   namespace   ReversibleLib
   11   {
   12       ///   <summary> 
  13       ///   使代码成为可逆框架的事务性代码
   14       ///   </summary> 
  15       public   class   ReversibleManagerScope : IDisposable
   16       {
   17           ///   <summary> 
  18           ///   初始化ReversibleManagerScope新的实例
   19           ///   </summary> 
  20           public   ReversibleManagerScope()
   21           {
   22              ReversibleManager._reversibleManager =  new   ReversibleManager();
   23           }
   24           ///   <summary> 
  25           ///   使用ReversibleManager对象构造ReversibleManagerScope使用范围对象
   26           ///   </summary> 
  27           ///   <param name="manager">  ReversibleManager实例  </param> 
  28           public   ReversibleManagerScope(ReversibleManager manager)
   29           {
   30              ReversibleManager._reversibleManager =  manager;
   31           }
   32           ///   <summary> 
  33           ///   使用自定义资源管理器构造ReversibleManagerScope包装的环境ReversibleManager.Current中的对象实例。
   34           ///   </summary> 
  35           ///   <param name="source">  IEnlistmentNotification资源管理器  </param> 
  36           public   ReversibleManagerScope(IEnlistmentNotification source)
   37           {
   38              ReversibleManager._reversibleManager =  new   ReversibleManager(source);
   39           }
   40           ///   <summary> 
  41           ///   全局上下文ReversibleManager对象销毁
   42           ///   </summary> 
  43           public   void   Dispose()
   44           {
   45              ReversibleManager._reversibleManager =  null  ;
   46           }
   47           ///   <summary> 
  48           ///   完成整个操作的提交。该操作将提交事务栈中的所有依赖事务
   49           ///   </summary> 
  50           public   void   Completed()
   51           {
   52               ReversibleManager.Current.Commit();
   53           }
   54       }
   55       ///   <summary> 
  56       ///   可逆模块的入口。
   57       ///   ReversibleManager对事务对象的封装,实现阶段性的事务提交和回滚。
   58       ///   </summary> 
  59       public   class   ReversibleManager
   60       {
   61           #region  上下文静态ReversibleManager实例
  62           ///   <summary> 
  63           ///   持有对可逆框架的对象引用
   64           ///   </summary> 
  65           internal   static   ReversibleManager _reversibleManager;
   66           ///   <summary> 
  67           ///   获取当前上下文中可逆框架
   68           ///   </summary> 
  69           public   static   ReversibleManager Current
   70           {
   71               get  {  return   _reversibleManager; }
   72           }
   73           #endregion 
  74  
  75           #region  构造对象
  76           ///   <summary> 
  77           ///   默认构造函数
   78           ///   </summary> 
  79           public   ReversibleManager() { }
   80           ///   <summary> 
  81           ///   表示可提交的事务(主事务)
   82           ///   </summary> 
  83           private   CommittableTransaction _commiTransaction;
   84           ///   <summary> 
  85           ///   支持两阶段提交协议的资源管理器(主资源管理器)
   86           ///   </summary> 
  87           private   IEnlistmentNotification _resourceManager;
   88           ///   <summary> 
  89           ///   重载构造函数,使用自定义资源管理器构造可逆模块的开始。
   90           ///   </summary> 
  91           ///   <param name="resource">  IEnlistmentNotification接口对象  </param> 
  92           public   ReversibleManager(IEnlistmentNotification resource)
   93           {
   94              _resourceManager =  resource;
   95               InitLoad(IsolationLevel.Serializable);
   96           }
   97           ///   <summary> 
  98           ///   重载构造函数,使用自定义资源管理器、内部事务范围的事务隔离级别构造可逆模型的开始。
   99           ///   </summary> 
 100           ///   <param name="resource">  IEnlistmentNotification接口对象  </param> 
 101           ///   <param name="isolationlevel">  IsolationLevel枚举成员  </param> 
 102           public   ReversibleManager(IEnlistmentNotification resource, IsolationLevel isolationlevel)
  103           {
  104              _resourceManager =  resource;
  105               InitLoad(isolationlevel);
  106           }
  107           ///   <summary> 
 108           ///   事务初始化阶段的参数对象
  109           ///   </summary> 
 110           TransactionOptions _options;
  111           ///   <summary> 
 112           ///   重载构造函数,使用自定义资源管理器、内部事务范围的事务隔离级别、事务超时时间范围构造可逆模块的开始。
  113           ///   </summary> 
 114           ///   <param name="resource">  IEnlistmentNotification接口对象  </param> 
 115           ///   <param name="isolationlevel">  IsolationLevel枚举成员  </param> 
 116           ///   <param name="span">  TimeSpan时间范围  </param> 
 117           public   ReversibleManager(IEnlistmentNotification resource, IsolationLevel isolationlevel, TimeSpan span)
  118           {
  119              _options =  new   TransactionOptions();
  120              _options.Timeout =  span;
  121               InitLoad(isolationlevel);
  122           }
  123           ///   <summary> 
 124           ///   构造CommittableTransaction对象实例。
  125           ///   </summary> 
 126           ///   <param name="level">  事务隔离级别  </param> 
 127           private   void   InitLoad(IsolationLevel level)
  128           {
  129               if  (_options ==  null  )
  130                  _options =  new   TransactionOptions();
  131              _options.IsolationLevel =  level;
  132              _commiTransaction =  new   CommittableTransaction(_options);
  133               _commiTransaction.EnlistVolatile(_resourceManager, EnlistmentOptions.None);
  134               //  作为事务栈的头开始整个可逆结构。 
 135              _tranStack.Push(_commiTransaction); //  压入事务栈 
 136              _resourceStack.Push(_resourceManager); //  压入资源栈
  137               //  设置环境事务,让所有支持事务性感知框架的代码都能执行。 
 138              Transaction.Current =  _commiTransaction;
  139           }
  140           #endregion 
 141  
 142           ///   <summary> 
 143           ///   事务栈,依次存放事务。
  144           ///   </summary> 
 145           private  System.Collections.Generic.Stack<Transaction> _tranStack =  new  Stack<Transaction> ();
  146           ///   <summary> 
 147           ///   资源栈,依次存放事务使用的资源。
  148           ///   </summary> 
 149           private  System.Collections.Generic.Stack<IEnlistmentNotification> _resourceStack =  new  Stack<IEnlistmentNotification> ();
  150           ///   <summary> 
 151           ///   阶段性事件委托
  152           ///   </summary> 
 153           ///   <param name="tran">  Transaction环境事务  </param> 
 154           public   delegate   void   PhaseHanlder(System.Transactions.Transaction tran);
  155           ///   <summary> 
 156           ///   下一步事件
  157           ///   </summary> 
 158           public   event   PhaseHanlder NextEvent;
  159           ///   <summary> 
 160           ///   上一步事件
  161           ///   </summary> 
 162           public   event   PhaseHanlder PreviousEvent;
  163           ///   <summary> 
 164           ///   开始下一步操作
  165           ///   </summary> 
 166           ///   <typeparam name="S">  IEnlistmentNotification接口实现  </typeparam> 
 167           ///   <param name="level">  IsolationLevel事务的隔离级别(对全局事务处理设置)  </param> 
 168           ///   <param name="source">  下一步操作的自定义数据管理器  </param> 
 169           public   void  Next<S> (IsolationLevel level, S source)
  170               where  S :  class ,IEnlistmentNotification,  new  ()
  171           {
  172              Transaction tran = _tranStack.Peek(); //  获取事务栈的顶端事务 
 173               if  (tran ==  null  )
  174                  tran = Transaction.Current; //  主事务 
 175              DependentTransaction depentran =  tran.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
  176               //  将本次事务处理的资源管理器压入资源栈中 
 177               depentran.EnlistVolatile(source, EnlistmentOptions.None);
  178               _tranStack.Push(depentran);
  179               _resourceStack.Push(source);
  180               //  切换环境事务场景 
 181              Transaction.Current =  depentran;
  182               if  (NextEvent !=  null  )
  183                   if  (NextEvent.GetInvocationList().Length >  0  )
  184                       NextEvent(Transaction.Current);
  185           }
  186           ///   <summary> 
 187           ///   返回上一步操作
  188           ///   </summary> 
 189           ///   <typeparam name="T">  需要接受的数据对象类型  </typeparam> 
 190           ///   <param name="refadd">  需要接受的数据对象引用  </param> 
 191           public   void  Previous<T>( out  T refadd)  where  T :  class , new  ()
  192           {
  193              Transaction tran =  _tranStack.Pop();
  194               if  (tran ==  null ) //  顶层事务 
 195                   Transaction.Current.Rollback();
  196               //   tran.Rollback();  //  回滚本事务,将触发所有克隆事务的回滚。 
 197               if  (PreviousEvent !=  null  )
  198                   if  (PreviousEvent.GetInvocationList().Length >  0  )
  199                   {
  200                       //  设置上一步数据对象 
 201                      refadd = (_resourceStack.Pop()  as  IReversibleGetResourceData<T> ).GetPreviousData();
  202                       PreviousEvent(Transaction.Current);
  203                       return  ;
  204                   }
  205              refadd =  new  T(); //  事务处理异常 
 206           }
  207           ///   <summary> 
 208           ///   提交事物堆栈中的所有事物
  209           ///   </summary> 
 210           public   void   Commit()
  211           {
  212               if  (Transaction.Current  is   DependentTransaction)
  213                  (Transaction.Current  as   DependentTransaction).Complete();
  214               for  ( int  i =  0 ; i < _tranStack.Count -  1 ; i++ )
  215               {
  216                   //  依赖事务 
 217                  (_tranStack.Pop()  as   DependentTransaction).Complete();
  218               }
  219               //  提交事务,主事务。必须进行克隆主体的提交才能完成所有阶段的操作。 
 220              (_tranStack.Pop()  as   CommittableTransaction).Commit();
  221           }
  222           ///   <summary> 
 223           ///   回滚事物堆栈中的所有事物
  224           ///   </summary> 
 225           public   void   RollBack()
  226           {
  227               if  (Transaction.Current  is   DependentTransaction)
  228                  (Transaction.Current  as   DependentTransaction).Rollback();
  229               for  ( int  i =  0 ; i < _tranStack.Count -  1 ; i++ )
  230               {
  231                   //  依赖事务 
 232                  (_tranStack.Pop()  as   DependentTransaction).Rollback();
  233               }
  234               //  提交事务,主事务。必须进行克隆主体的提交才能完成所有阶段的操作。 
 235              (_tranStack.Pop()  as   CommittableTransaction).Rollback();
  236           }
  237       }
  238  }

3.示例

这里我使用了一个简单的String Builder作为资源管理器需要管理的对象。

请看代码:

View Code

 1   /*  **
   2    * author:深度训练
   3    * blog:  http://wangqingpei557.blog.51cto.com/ 
  4    * *  */ 
  5   using   System;
   6   using   System.Collections.Generic;
   7   using   System.Text;
   8   using   System.Data;
   9   using   System.Transactions;
  10   using   ReversibleLib;
  11  
 12   namespace   ConsoleApplication1
  13   {
  14       class   Program
  15       {
  16           static   void  Main( string  [] args)
  17           {
  18               //  构造数据 
 19              StringBuilder strbuilder =  new   StringBuilder();
  20              strbuilder.Append( "  0  " ); //  初始数据为0
  21  
 22               //  资源管理器 
 23              ReResourceManager<StringBuilder, StringBuilderCopy> strResource =
 24                   new  ReResourceManager<StringBuilder, StringBuilderCopy>(strbuilder,  new   StringBuilderCopy());
  25              strResource.Name =  "  0资源管理器  "  ;
  26               //  开始进入可逆框架处理环境 
 27               using  (ReversibleManagerScope reversible =  new   ReversibleManagerScope(strResource))
  28               {
  29                   try 
 30                   {
  31                      ReversibleManager.Current.PreviousEvent +=  new   ReversibleManager.PhaseHanlder(Current_PreviousEvent);
  32                      ReversibleManager.Current.NextEvent +=  new   ReversibleManager.PhaseHanlder(Current_NextEvent);
  33                      strbuilder.Append( "  1  " ); //  首次修改数据为01
  34  
 35                       //  获取下一步操作的数据 
 36                      StringBuilder strbuilder2 = (strResource  as  IReversibleGetResourceData<StringBuilder> ).GetNextData();
  37                       //  构造下一步操作的自定义资源管理器 
 38                      ReResourceManager<StringBuilder, StringBuilderCopy> strResource2 =
 39                           new  ReResourceManager<StringBuilder, StringBuilderCopy>(strbuilder2,  new   StringBuilderCopy());
  40                      strResource2.Name =  "  2资源管理器  "  ;
  41                      ReversibleManager.Current.Next<ReResourceManager<StringBuilder, StringBuilderCopy>> (
  42                           System.Transactions.IsolationLevel.Serializable, strResource2);
  43                      strbuilder2.Append( "  2  " ); //  第二步修改数据为012
  44  
 45                       //  返回上一步,也就是回滚对数据进行“2”设置的前一个状态 
 46                       StringBuilder strbuilder3;
  47                      ReversibleManager.Current.Previous<StringBuilder>( out  strbuilder3); //  获取上一步使用的数据,这里应该是01 
 48  
 49                      reversible.Completed(); //  提交所有操作 
 50                       Console.WriteLine(strbuilder3);
  51                   }
  52                   catch   (Exception err)
  53                   { Console.WriteLine(err.Message); ReversibleManager.Current.RollBack(); }
  54               }
  55               Console.ReadLine();
  56           }
  57  
 58           static   void   Current_NextEvent(Transaction tran)
  59           {
  60              Console.WriteLine( "  下一步:  "  +  tran.TransactionInformation.LocalIdentifier);
  61              Console.WriteLine( "  下一步:  "  +  tran.TransactionInformation.DistributedIdentifier);
  62           }
  63           static   void   Current_PreviousEvent(Transaction tran)
  64           {
  65              Console.WriteLine( "  上一步:  "  +  tran.TransactionInformation.LocalIdentifier);
  66              Console.WriteLine( "  上一步:  "  +  tran.TransactionInformation.DistributedIdentifier);
  67           }
  68       }
  69  }

 这里我使用0作为资源的初始数据,然后进入到第一个环节,我将它附加了1,然后进入到第二个环节,我将它附加了2,这里应该是012了,但是下面我突然又返回到了上一步,所以最后的数据应该是01。如果我们需要使用复杂的数据对象,如常用的Data Table类型,我们一般都是用它来展现一组数据,然后对这组数据进行一系列的操作。

总结:

这篇文章主要是想介绍一下事务的另一种使用方式,对可逆框架的设计方向算是一个抛砖引玉吧,希望大家用的着。 [王清培版权所有,转载请给出署名]

源码地址: https://files.cnblogs.com/wangiqngpei557/Reversible.zip

 

分类:  系统架构设计

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于.NET可逆框架设计的详细内容...

  阅读:35次