淺談.NET可逆框架設(shè)計
1. 什么是可逆的程序框架
什么叫可逆的?程序的執(zhí)行是可以被無限制回滾的。
什么叫可逆的框架?實現(xiàn)了對可逆功能的封裝,并能通過簡單的接口調(diào)用進(jìn)行使用??蚣芸赡苡写笥行。蚁脒@么稱呼它是為了表達(dá)它的整體性和重要性。
那么到底可逆的需求在哪里?其實在我們開發(fā)程序的時候經(jīng)常會使用事務(wù)來進(jìn)行業(yè)務(wù)的控制。比如刪除訂單,然后刪除訂單明細(xì)等等,對于這樣的要求很多,我們只能將邏輯控制在一個事務(wù)范圍內(nèi),不能在沒有事務(wù)性的邏輯代碼中編寫這種要求的業(yè)務(wù)功能。等出現(xiàn)未知錯誤的時候在進(jìn)行事務(wù)的回滾。
你也許會問,使用原來的事務(wù)處理不是也能進(jìn)行回滾嗎?當(dāng)然不是這么簡單的,我們使用事務(wù)回滾時只能將資源回滾到最初未進(jìn)行事務(wù)處理前的狀態(tài)。(這里不僅僅指的是數(shù)據(jù)庫事務(wù),而是全局的事務(wù)處理) 我們用圖做個比較。
傳統(tǒng)的事務(wù)處理圖:

可逆的事務(wù)處理圖:

從這兩幅圖中我們可以很明顯的看出,傳統(tǒng)的事務(wù)處理在事務(wù)處理的過程當(dāng)中無法控制中間數(shù)據(jù),也就是說無法對事務(wù)處理進(jìn)行分段,然后在進(jìn)行統(tǒng)一的提交或回滾。
在可逆框架的事務(wù)處理里我們就可以控制事務(wù)的執(zhí)行階段,在必要的時候我們只需提交或者回滾某一階段的數(shù)據(jù)。
1.1環(huán)境事務(wù)
在可逆框架的事務(wù)處理圖中,我們看到事務(wù)的開始,然后就進(jìn)行下一步、下一步這樣的操作。在每進(jìn)行一個下一步操作的時候,就是進(jìn)入到了一個子事務(wù)里處理,在.NET中是可以進(jìn)行事務(wù)的嵌套,其實也就是依賴事務(wù)Dependent Transaction實現(xiàn)。通過使用環(huán)境事務(wù)可以讓事務(wù)性感知代碼能自動的識別出您將要使用事務(wù)進(jìn)行操作。所以在每進(jìn)行下一步操作的時候,只有將當(dāng)前環(huán)境事務(wù)切換為您將依賴的子事務(wù)才行。如果只是單純的使用依賴事務(wù)對象實例在使用,那么將無法進(jìn)行諸多其他的事務(wù)處理。
2可逆框架的實現(xiàn)原理
由于我們只能控制自定義事務(wù)資源管理器的內(nèi)部實現(xiàn),所以我們在構(gòu)建自己的數(shù)據(jù)處理時問題變的簡單多了。
實現(xiàn)可逆框架的核心技術(shù)就是使用依賴事務(wù)進(jìn)行事務(wù)的克隆操作。將一個大的事務(wù)處理邏輯上切割成多了小的事務(wù)操作,然后在進(jìn)行統(tǒng)一的提交或回滾。
在實現(xiàn)上其實就是將Committable Transaction對象進(jìn)行包裝,實現(xiàn)簡單的調(diào)用接口。這里參照了環(huán)境代碼的概念,將對象的生命周期控制在代碼片段中。
2.1自定義資源管理器的實現(xiàn)
我們需要擴(kuò)展IEnlistmentNotification接口的實現(xiàn),加入對“上一步”、“下一步”的數(shù)據(jù)操作。
請看代碼:
- /***
 - * author:深度訓(xùn)練
 - * blog:http://wangqingpei557.blog.51cto.com/
 - * **/
 - using System;
 - using System.Collections.Generic;
 - using System.Text;
 - using System.Transactions;
 - namespace ReversibleLib
 - {
 - /// <summary>
 - /// 可逆范圍內(nèi)的資源管理器。
 - /// 可以使用該類對易失性資源進(jìn)行事務(wù)范圍內(nèi)的管理。在事務(wù)操作范圍內(nèi)進(jìn)行可逆操作。
 - /// </summary>
 - /// <typeparam name="T">需要管理的資源類型</typeparam>
 - /// <typeparam name="Xcopy">資源在使用、恢復(fù)過程中的數(shù)據(jù)復(fù)制對象。</typeparam>
 - public class ReResourceManager<T, Xcopy> : IEnlistmentNotification, IReversibleGetResourceData<T>
 - where T : class, new()
 - where Xcopy : class
 - {
 - /// <summary>
 - /// 私有字段。資源的持久引用。
 - /// </summary>
 - T _commitfrontvalue;
 - /// <summary>
 - /// 私有字段。事務(wù)性操作數(shù)據(jù)對象。
 - /// </summary>
 - T _rollbackfrontvalue = new T();
 - /// <summary>
 - /// 保存數(shù)據(jù)復(fù)制對象。
 - /// </summary>
 - Xcopy _copy;
 - /// <summary>
 - /// 泛型約束需要,內(nèi)部使用。
 - /// </summary>
 - public ReResourceManager() { }
 - /// <summary>
 - /// 資源管理器內(nèi)部名稱。便于追蹤
 - /// </summary>
 - public string Name { get; set; }
 - /// <summary>
 - /// 重載默認(rèn)構(gòu)造函數(shù),使用資源類型和數(shù)據(jù)復(fù)制對象初始化資源管理器。
 - /// </summary>
 - public ReResourceManager(T t, Xcopy icopy)
 - {
 - (icopy as IResourceCopy<T>).Copy(_rollbackfrontvalue, t);
 - _commitfrontvalue = t;
 - _copy = icopy;
 - }
 - #region IEnlistmentNotification 成員
 - public void Prepare(PreparingEnlistment preparingEnlistment)
 - {
 - preparingEnlistment.Prepared();
 - }
 - public void Commit(Enlistment enlistment)
 - {
 - enlistment.Done();
 - }
 - public void InDoubt(Enlistment enlistment)
 - {
 - enlistment.Done();
 - }
 - public void Rollback(Enlistment enlistment)
 - {
 - (_copy as IResourceCopy<T>).Copy(_commitfrontvalue, _rollbackfrontvalue);//回滾事務(wù)
 - enlistment.Done();
 - }
 - #endregion
 - #region IReversibleGetResourceData<T> 成員
 - T IReversibleGetResourceData<T>.GetPreviousData()
 - {
 - T result = new T();
 - (_copy as IResourceCopy<T>).Copy(result, _rollbackfrontvalue);
 - return result;
 - }
 - T IReversibleGetResourceData<T>.GetNextData()
 - {
 - T result = new T();
 - (_copy as IResourceCopy<T>).Copy(result, _commitfrontvalue);
 - return result;
 - }
 - #endregion
 - }
 - }
 
2.2可逆框架的入口實現(xiàn)
我們需要簡單的調(diào)用就能方便的使用可逆功能,不能以一種新的方式使用。所以這里借鑒了Transaction Scope的設(shè)計思想。
請看代碼:
- View Code
 - /***
 - * author:深度訓(xùn)練
 - * blog:http://wangqingpei557.blog.51cto.com/
 - * **/
 - using System;
 - using System.Collections.Generic;
 - using System.Text;
 - using System.Transactions;
 - namespace ReversibleLib
 - {
 - /// <summary>
 - /// 使代碼成為可逆框架的事務(wù)性代碼
 - /// </summary>
 - public class ReversibleManagerScope : IDisposable
 - {
 - /// <summary>
 - /// 初始化ReversibleManagerScope新的實例
 - /// </summary>
 - public ReversibleManagerScope()
 - {
 - ReversibleManager._reversibleManager = new ReversibleManager();
 - }
 - /// <summary>
 - /// 使用ReversibleManager對象構(gòu)造ReversibleManagerScope使用范圍對象
 - /// </summary>
 - /// <param name="manager">ReversibleManager實例</param>
 - public ReversibleManagerScope(ReversibleManager manager)
 - {
 - ReversibleManager._reversibleManager = manager;
 - }
 - /// <summary>
 - /// 使用自定義資源管理器構(gòu)造ReversibleManagerScope包裝的環(huán)境ReversibleManager.Current中的對象實例。
 - /// </summary>
 - /// <param name="source">IEnlistmentNotification資源管理器</param>
 - public ReversibleManagerScope(IEnlistmentNotification source)
 - {
 - ReversibleManager._reversibleManager = new ReversibleManager(source);
 - }
 - /// <summary>
 - /// 全局上下文ReversibleManager對象銷毀
 - /// </summary>
 - public void Dispose()
 - {
 - ReversibleManager._reversibleManager = null;
 - }
 - /// <summary>
 - /// 完成整個操作的提交。該操作將提交事務(wù)棧中的所有依賴事務(wù)
 - /// </summary>
 - public void Completed()
 - {
 - ReversibleManager.Current.Commit();
 - }
 - }
 - /// <summary>
 - /// 可逆模塊的入口。
 - /// ReversibleManager對事務(wù)對象的封裝,實現(xiàn)階段性的事務(wù)提交和回滾。
 - /// </summary>
 - public class ReversibleManager
 - {
 - #region 上下文靜態(tài)ReversibleManager實例
 - /// <summary>
 - /// 持有對可逆框架的對象引用
 - /// </summary>
 - internal static ReversibleManager _reversibleManager;
 - /// <summary>
 - /// 獲取當(dāng)前上下文中可逆框架
 - /// </summary>
 - public static ReversibleManager Current
 - {
 - get { return _reversibleManager; }
 - }
 - #endregion
 - #region 構(gòu)造對象
 - /// <summary>
 - /// 默認(rèn)構(gòu)造函數(shù)
 - /// </summary>
 - public ReversibleManager() { }
 - /// <summary>
 - /// 表示可提交的事務(wù)(主事務(wù))
 - /// </summary>
 - private CommittableTransaction _commiTransaction;
 - /// <summary>
 - /// 支持兩階段提交協(xié)議的資源管理器(主資源管理器)
 - /// </summary>
 - private IEnlistmentNotification _resourceManager;
 - /// <summary>
 - /// 重載構(gòu)造函數(shù),使用自定義資源管理器構(gòu)造可逆模塊的開始。
 - /// </summary>
 - /// <param name="resource">IEnlistmentNotification接口對象</param>
 - public ReversibleManager(IEnlistmentNotification resource)
 - {
 - _resourceManager = resource;
 - InitLoad(IsolationLevel.Serializable);
 - }
 - /// <summary>
 - /// 重載構(gòu)造函數(shù),使用自定義資源管理器、內(nèi)部事務(wù)范圍的事務(wù)隔離級別構(gòu)造可逆模型的開始。
 - /// </summary>
 - /// <param name="resource">IEnlistmentNotification接口對象</param>
 - /// <param name="isolationlevel">IsolationLevel枚舉成員</param>
 - public ReversibleManager(IEnlistmentNotification resource, IsolationLevel isolationlevel)
 - {
 - _resourceManager = resource;
 - InitLoad(isolationlevel);
 - }
 - /// <summary>
 - /// 事務(wù)初始化階段的參數(shù)對象
 - /// </summary>
 - TransactionOptions _options;
 - /// <summary>
 - /// 重載構(gòu)造函數(shù),使用自定義資源管理器、內(nèi)部事務(wù)范圍的事務(wù)隔離級別、事務(wù)超時時間范圍構(gòu)造可逆模塊的開始。
 - /// </summary>
 - /// <param name="resource">IEnlistmentNotification接口對象</param>
 - /// <param name="isolationlevel">IsolationLevel枚舉成員</param>
 - /// <param name="span">TimeSpan時間范圍</param>
 - public ReversibleManager(IEnlistmentNotification resource, IsolationLevel isolationlevel, TimeSpan span)
 - {
 - _options = new TransactionOptions();
 - _options.Timeout = span;
 - InitLoad(isolationlevel);
 - }
 - /// <summary>
 - /// 構(gòu)造CommittableTransaction對象實例。
 - /// </summary>
 - /// <param name="level">事務(wù)隔離級別</param>
 - private void InitLoad(IsolationLevel level)
 - {
 - if (_options == null)
 - _options = new TransactionOptions();
 - _options.IsolationLevel = level;
 - _commiTransaction = new CommittableTransaction(_options);
 - _commiTransaction.EnlistVolatile(_resourceManager, EnlistmentOptions.None);
 - //作為事務(wù)棧的頭開始整個可逆結(jié)構(gòu)。
 - _tranStack.Push(_commiTransaction);//壓入事務(wù)棧
 - _resourceStack.Push(_resourceManager);//壓入資源棧
 - //設(shè)置環(huán)境事務(wù),讓所有支持事務(wù)性感知框架的代碼都能執(zhí)行。
 - Transaction.Current = _commiTransaction;
 - }
 - #endregion
 - /// <summary>
 - /// 事務(wù)棧,依次存放事務(wù)。
 - /// </summary>
 - private System.Collections.Generic.Stack<Transaction> _tranStack = new Stack<Transaction>();
 - /// <summary>
 - /// 資源棧,依次存放事務(wù)使用的資源。
 - /// </summary>
 - private System.Collections.Generic.Stack<IEnlistmentNotification> _resourceStack = new Stack<IEnlistmentNotification>();
 - /// <summary>
 - /// 階段性事件委托
 - /// </summary>
 - /// <param name="tran">Transaction環(huán)境事務(wù)</param>
 - public delegate void PhaseHanlder(System.Transactions.Transaction tran);
 - /// <summary>
 - /// 下一步事件
 - /// </summary>
 - public event PhaseHanlder NextEvent;
 - /// <summary>
 - /// 上一步事件
 - /// </summary>
 - public event PhaseHanlder PreviousEvent;
 - /// <summary>
 - /// 開始下一步操作
 - /// </summary>
 - /// <typeparam name="S">IEnlistmentNotification接口實現(xiàn)</typeparam>
 - /// <param name="level">IsolationLevel事務(wù)的隔離級別(對全局事務(wù)處理設(shè)置)</param>
 - /// <param name="source">下一步操作的自定義數(shù)據(jù)管理器</param>
 - public void Next<S>(IsolationLevel level, S source)
 - where S : class,IEnlistmentNotification, new()
 - {
 - Transaction tran = _tranStack.Peek();//獲取事務(wù)棧的頂端事務(wù)
 - if (tran == null)
 - tran = Transaction.Current;//主事務(wù)
 - DependentTransaction depentran = tran.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
 - //將本次事務(wù)處理的資源管理器壓入資源棧中
 - depentran.EnlistVolatile(source, EnlistmentOptions.None);
 - _tranStack.Push(depentran);
 - _resourceStack.Push(source);
 - //切換環(huán)境事務(wù)場景
 - Transaction.Current = depentran;
 - if (NextEvent != null)
 - if (NextEvent.GetInvocationList().Length > 0)
 - NextEvent(Transaction.Current);
 - }
 - /// <summary>
 - /// 返回上一步操作
 - /// </summary>
 - /// <typeparam name="T">需要接受的數(shù)據(jù)對象類型</typeparam>
 - /// <param name="refadd">需要接受的數(shù)據(jù)對象引用</param>
 - public void Previous<T>(out T refadd) where T : class,new()
 - {
 - Transaction tran = _tranStack.Pop();
 - if (tran == null)//頂層事務(wù)
 - Transaction.Current.Rollback();
 - // tran.Rollback();//回滾本事務(wù),將觸發(fā)所有克隆事務(wù)的回滾。
 - if (PreviousEvent != null)
 - if (PreviousEvent.GetInvocationList().Length > 0)
 - {
 - //設(shè)置上一步數(shù)據(jù)對象
 - refadd = (_resourceStack.Pop() as IReversibleGetResourceData<T>).GetPreviousData();
 - PreviousEvent(Transaction.Current);
 - return;
 - }
 - refadd = new T();//事務(wù)處理異常
 - }
 - /// <summary>
 - /// 提交事物堆棧中的所有事物
 - /// </summary>
 - public void Commit()
 - {
 - if (Transaction.Current is DependentTransaction)
 - (Transaction.Current as DependentTransaction).Complete();
 - for (int i = 0; i < _tranStack.Count - 1; i++)
 - {
 - //依賴事務(wù)
 - (_tranStack.Pop() as DependentTransaction).Complete();
 - }
 - //提交事務(wù),主事務(wù)。必須進(jìn)行克隆主體的提交才能完成所有階段的操作。
 - (_tranStack.Pop() as CommittableTransaction).Commit();
 - }
 - /// <summary>
 - /// 回滾事物堆棧中的所有事物
 - /// </summary>
 - public void RollBack()
 - {
 - if (Transaction.Current is DependentTransaction)
 - (Transaction.Current as DependentTransaction).Rollback();
 - for (int i = 0; i < _tranStack.Count - 1; i++)
 - {
 - //依賴事務(wù)
 - (_tranStack.Pop() as DependentTransaction).Rollback();
 - }
 - //提交事務(wù),主事務(wù)。必須進(jìn)行克隆主體的提交才能完成所有階段的操作。
 - (_tranStack.Pop() as CommittableTransaction).Rollback();
 - }
 - }
 - }
 
3.示例
這里我使用了一個簡單的String Builder作為資源管理器需要管理的對象。
請看代碼:
- View Code
 - /***
 - * author:深度訓(xùn)練
 - * blog:http://wangqingpei557.blog.51cto.com/
 - * **/
 - using System;
 - using System.Collections.Generic;
 - using System.Text;
 - using System.Data;
 - using System.Transactions;
 - using ReversibleLib;
 - namespace ConsoleApplication1
 - {
 - class Program
 - {
 - static void Main(string[] args)
 - {
 - //構(gòu)造數(shù)據(jù)
 - StringBuilder strbuilder = new StringBuilder();
 - strbuilder.Append("0");//初始數(shù)據(jù)為0
 - //資源管理器
 - ReResourceManager<StringBuilder, StringBuilderCopy> strResource =
 - new ReResourceManager<StringBuilder, StringBuilderCopy>(strbuilder, new StringBuilderCopy());
 - strResource.Name = "0資源管理器";
 - //開始進(jìn)入可逆框架處理環(huán)境
 - using (ReversibleManagerScope reversible = new ReversibleManagerScope(strResource))
 - {
 - try
 - {
 - ReversibleManager.Current.PreviousEvent += new ReversibleManager.PhaseHanlder(Current_PreviousEvent);
 - ReversibleManager.Current.NextEvent += new ReversibleManager.PhaseHanlder(Current_NextEvent);
 - strbuilder.Append("1");//***修改數(shù)據(jù)為01
 - //獲取下一步操作的數(shù)據(jù)
 - StringBuilder strbuilder2 = (strResource as IReversibleGetResourceData<StringBuilder>).GetNextData();
 - //構(gòu)造下一步操作的自定義資源管理器
 - ReResourceManager<StringBuilder, StringBuilderCopy> strResource2 =
 - new ReResourceManager<StringBuilder, StringBuilderCopy>(strbuilder2, new StringBuilderCopy());
 - strResource2.Name = "2資源管理器";
 - ReversibleManager.Current.Next<ReResourceManager<StringBuilder, StringBuilderCopy>>(
 - System.Transactions.IsolationLevel.Serializable, strResource2);
 - strbuilder2.Append("2");//第二步修改數(shù)據(jù)為012
 - //返回上一步,也就是回滾對數(shù)據(jù)進(jìn)行“2”設(shè)置的前一個狀態(tài)
 - StringBuilder strbuilder3;
 - ReversibleManager.Current.Previous<StringBuilder>(out strbuilder3);//獲取上一步使用的數(shù)據(jù),這里應(yīng)該是01
 - reversible.Completed();//提交所有操作
 - Console.WriteLine(strbuilder3);
 - }
 - catch (Exception err)
 - { Console.WriteLine(err.Message); ReversibleManager.Current.RollBack(); }
 - }
 - Console.ReadLine();
 - }
 - static void Current_NextEvent(Transaction tran)
 - {
 - Console.WriteLine("下一步:" + tran.TransactionInformation.LocalIdentifier);
 - Console.WriteLine("下一步:" + tran.TransactionInformation.DistributedIdentifier);
 - }
 - static void Current_PreviousEvent(Transaction tran)
 - {
 - Console.WriteLine("上一步:" + tran.TransactionInformation.LocalIdentifier);
 - Console.WriteLine("上一步:" + tran.TransactionInformation.DistributedIdentifier);
 - }
 - }
 - }
 
這里我使用0作為資源的初始數(shù)據(jù),然后進(jìn)入到***個環(huán)節(jié),我將它附加了1,然后進(jìn)入到第二個環(huán)節(jié),我將它附加了2,這里應(yīng)該是012了,但是下面我突然又返回到了上一步,所以***的數(shù)據(jù)應(yīng)該是01。如果我們需要使用復(fù)雜的數(shù)據(jù)對象,如常用的Data Table類型,我們一般都是用它來展現(xiàn)一組數(shù)據(jù),然后對這組數(shù)據(jù)進(jìn)行一系列的操作。
總結(jié):
這篇文章主要是想介紹一下事務(wù)的另一種使用方式,對可逆框架的設(shè)計方向算是一個拋磚引玉吧,希望大家用的著。
源碼地址:http://files.cnblogs.com/wangiqngpei557/Reversible.zip
原文:http://www.cnblogs.com/wangiqngpei557/archive/2012/06/24/2560576.html
【編輯推薦】















 
 
 







 
 
 
 