.NET簡談自定義事務資源管理器


在上一篇文章“NET簡談事務、分布式事務處理”中我大概總結了關於.NET中的事務處理方式和結合了WCF框架的簡單應用。在事務性操作中我們的重點是能將數據進行可逆化,說白了就是能保證數據的ACID(關於事務的整體模型、原理請參見“.NET簡談事務本質論”一文),在.NET事務處理框架中強大的類庫幫我們實現了很多事務傳遞、事務自動提升的技術難點,同時也提供了很多擴展接口,只要我們肯去研究總能有收獲。

這篇文章主要講解怎樣利用.NET為我們提供的擴展接口進行自定義的事務處理范圍內的資源管理,在事務的操作范圍內我們不會總是將數據庫視為依賴的對象,也不會總是IdbTransaction之類的對象,我們需要自己的事務性資源管理器,我們需要自己的持久性資源管理器。在可能的情況下我們需要自己開發后備持久存儲區。

有興趣的朋友可以去看看我們蔣金楠蔣大哥的關於事務的文章,講的非常好:http://www.cnblogs.com/artech/archive/2010/01/31/1660433.html

下面我們將實現一個簡單的事務性資源管理器,在此先解釋一下關於事務性資源管理器的意思。在我們日常開發過程中,大部分的數據都是存儲於數據庫中,事務范圍內的操作不允許對非事務性資源進行修改因為他們是不可逆的,沒有資源管理器對他們進行管理,當事務出錯時無法將修改后的數據恢復到事務操作之前的狀態,我們只能對數據庫中的數據進行修改然后執行回滾,因為數據庫中的數據有數據庫資源管理器進行強大的管理。[王清培版權所有,轉載請給出署名]

當我們使用IdbTransaction進行事務處理時其實是獲取對遠程事務處理的一個引用,比如SqlTransaction對象他就是邏輯上的事務資源管理器,當我們使用TransactionScope進行事務范圍操作時,SqlServer數據提供程序能進行自動的事務提升並且進行事務資源登記,在最后能很好的進行二階段提交協議進行數據的最終提交。

 

事務性資源管理器分類:

.NET事務模型提供了幾個接口方便我們實現自定義的資源管理器,我們可以通過繼承這些接口實現支持單階段、兩階段提交協議的資源管理器。

1.IenlistmentNotification接口:支持兩階段提交協議的資源管理器實現接口。

(官方解釋:描述資源管理器為了在登記參與時為事務管理器提供兩階段提交通知回調而應該實現的接口。)

2.IsinglePhaseNotification接口:支持單階段協議的資源管理器實現接口。

(官方解釋:描述支持單階段提交優化以參與事務的資源對象。)

3. IpromotableSinglePhaseNotification接口:支持可提升的單階段提交協議的資源管理器實現接口。(官方解釋:描述作為資源管理器內部非分布式事務的提交委托的對象。)這個對象繼承自ItransactionPromoter接口,該接口需要自動提升為由MSDTC管理的資源管理器使用的。

 

實現System.Transactions.IenlistmentNotification接口,自定義兩階段提交協議的資源管理器

下面我們通過實現IenlistmentNotification接口來開發一個簡單的資源管理器。

代碼1:資源管理器

public class IEnlistmentNotificationDemo<T, Xcopy> : IEnlistmentNotification
        where T : new()
        where Xcopy : class
    {
        T _commitfrontvalue;
        T _rollbackfrontvalue = new T();
        Xcopy copy;
        public IEnlistmentNotificationDemo(T t, Xcopy icopy)
        {
            (icopy as IResourceCopy<T>).Copy(_rollbackfrontvalue, t);
            _commitfrontvalue = t;//保持對資源修改的引用
            copy = icopy;
        }

        #region IEnlistmentNotification 成員
        public void Prepare(PreparingEnlistment preparingEnlistment)
        {
            //兩階段提交協議中的准備階段
            Console.WriteLine("准備提交");
            ConsoleKeyInfo key = Console.ReadKey();
            if (key.KeyChar == 'Y' || key.KeyChar == 'y')
            {
                preparingEnlistment.Prepared();  //投票提交事務
            }
            else if (key.KeyChar == 'N' || key.KeyChar == 'n')
            {
                Console.WriteLine("\n由我投票整個事務回滾:" + _rollbackfrontvalue);
                (copy as IResourceCopy<T>).Copy(_commitfrontvalue, _rollbackfrontvalue);//回滾事務自願
                preparingEnlistment.ForceRollback();//投票回滾事務,資源管理發生錯誤時因該將其自動回復數據,
                //因為事務管理不會通知發生ForceRollback()方法的管理器。
            }
        }
        public void Commit(Enlistment enlistment)
        {
            //嘗試提交
            Console.WriteLine("事務嘗試提交");
            enlistment.Done();//通知事務管理器,參與者已完成提交工作。
        }
        public void Rollback(Enlistment enlistment)
        {
            //事務激活階段處理錯誤,執行回滾
            Console.WriteLine("操作失敗,回滾" + _rollbackfrontvalue);
            (copy as IResourceCopy<T>).Copy(_commitfrontvalue, _rollbackfrontvalue);//回滾事務自願
            enlistment.Done();
        }
        public void InDoubt(Enlistment enlistment)
        {
            //與其他的資源管理器失去聯系
            Console.WriteLine("與其他的資源管理器失去聯系,通常記錄日志");
            enlistment.Done();
        }
        #endregion
    }

 

該類是支持兩階段提交協議的資源管理器,由於不同的資源面臨這不同的數據復制操作。當我們進行初始數據備份的時候需要對不同的數據類型進行不同的數據復制操作,所以我們需要一個規范來進行約定。

代碼2:資源復制接口

/// <summary>
    /// 事務性資源管理器中的資源拷貝,不同的資源類型存在多種拷貝形式。
    /// </summary>
    public interface IResourceCopy<T>
    {
        void Copy(T t1, T t2);
}

 

代碼3:實現StringBuilder類型的數據復制

/// <summary>
    /// StringBuilder類型的數據Copy對象,需要實現IResourceCopy泛型接口。
    /// </summary>
    public class StringBuilderCopy : IResourceCopy<StringBuilder>
    {

        #region IResourceCopy<StringBuilder> 成員

        public void Copy(StringBuilder t1, StringBuilder t2)
        {
            t1.Remove(0, t1.Length);
            t1.Append(t2.ToString());
        }

        #endregion
}

 

這樣我們就有了對於StringBuilder類型的數據復制操作類。

代碼4:將自定義的資源管理器參與到事務處理中

/// <summary>
    /// 事務范圍內的登記資源管理對象的狀態
    /// </summary>
    public class EnlistmentDemo
    {
        [MethodImpl(MethodImplOptions.Synchronized)]//鎖住當前方法,避免多線程訪問破壞事務資源的一致性。
        public void Start()
        {
            //易失性資源
            StringBuilder stringvalues = new StringBuilder("123");
            StringBuilder stringvalues2 = new StringBuilder("456");
            StringBuilder stringvalues3 = new StringBuilder("789");
            StringBuilder stringvalues4 = new StringBuilder("101112");
            //使用資源管理器進行管理
            IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource =
                new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues, new StringBuilderCopy());
            IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource2 =
                 new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues2, new StringBuilderCopy());
            IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource3 =
                new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues3, new StringBuilderCopy());
            IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource4 =
                 new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues4, new StringBuilderCopy());
            try
            {
                using (TransactionScope transcope = new TransactionScope())
                {
                    Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(commitran_TransactionCompleted);
                    //登記事務資源管理器,在開始一切事務性操作之前必須先登記資源管理器
                    Transaction.Current.EnlistVolatile(resource, EnlistmentOptions.None);
                    Transaction.Current.EnlistVolatile(resource2, EnlistmentOptions.None);
                    Transaction.Current.EnlistVolatile(resource3, EnlistmentOptions.None);
                    Transaction.Current.EnlistVolatile(resource4, EnlistmentOptions.None);
                    //開始事務性操作,該階段屬於事務的激活階段。
                    stringvalues.Append("456");
                    stringvalues2.Append("789");
                    stringvalues3.Append("101112");
                    stringvalues4.Append("131415");
                    transcope.Complete();//開始兩階段提交,該階段屬於事務的准備階段。
                }
            }
            catch { Console.WriteLine("事務執行出錯,執行回滾"); }

            //查看被事務性操作后的值
            Console.WriteLine("事務完成后的結果值:");
            Console.WriteLine(stringvalues + "|" + stringvalues2 + "|" + stringvalues3 + "|" + stringvalues4);
        }

        //事務結束時觸發的事件方法,可以捕獲事務執行結果。
        void commitran_TransactionCompleted(object sender, TransactionEventArgs e)
        {
            Console.WriteLine("transaction completed:");
            Console.WriteLine("ID:             {0}", e.Transaction.TransactionInformation.LocalIdentifier);
            Console.WriteLine("Distributed ID: {0}", e.Transaction.TransactionInformation.DistributedIdentifier);
            Console.WriteLine("Status:         {0}", e.Transaction.TransactionInformation.Status);
            Console.WriteLine("IsolationLevel: {0}", e.Transaction.IsolationLevel);
        }
    }

 

我們來看一下效果:

圖1:事務提交

圖2:事務回滾:

這樣我們就能很好的將自定義的資源管理器參與到事務處理當中來,對於分布式的事務處理其實也是一樣的,在事務的操作范圍內首先進行資源管理器的登記才能使用。

事務處理是一個龐大的領域,我這里只是一個小小的應用。希望大家能用的着。[王清培版權所有,轉載請給出署名]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM