Fork me on GitHub

.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:事务回滚:

这样我们就能很好的将自定义的资源管理器参与到事务处理当中来,对于分布式的事务处理其实也是一样的,在事务的操作范围内首先进行资源管理器的登记才能使用。

事务处理是一个庞大的领域,我这里只是一个小小的应用。希望大家能用的着。[王清培版权所有,转载请给出署名]

 

posted @ 2012-01-02 20:03 王清培 阅读(...) 评论(...) 编辑 收藏