.NET简谈自定义事务资源管理器

简介:

在上一篇文章“NET简谈事务、分布式事务处理”中我大概总结了关于.NET中的事务处理方式和结合了WCF框架的简单应用。在事务性操作中我们的重点是能将数据进行可逆化,说白了就是能保证数据的ACID(关于事务的整体模型、原理请参见“.NET简谈事务本质论”一文),在.NET事务处理框架中强大的类库帮我们实现了很多事务传递、事务自动提升的技术难点,同时也提供了很多扩展接口,只要我们肯去研究总能有收获。[王清培版权所有,转载请给出署名]

这篇文章主要讲解怎样利用.NET为我们提供的扩展接口进行自定义的事务处理范围内的资源管理,在事务的操作范围内我们不会总是将数据库视为依赖的对象,也不会总是IdbTransaction之类的对象,我们需要自己的事务性资源管理器,我们需要自己的持久性资源管理器。在可能的情况下我们需要自己开发后备持久存储区。
 
有兴趣的朋友可以去看看我们蒋金楠蒋大哥的关于事务的文章,讲的非常好:
 
下面我们将实现一个简单的事务性资源管理器,在此先解释一下关于事务性资源管理器的意思。在我们日常开发过程中,大部分的数据都是存储于数据库中,事务范围内的操作不允许对非事务性资源进行修改因为他们是不可逆的,没有资源管理器对他们进行管理,当事务出错时无法将修改后的数据恢复到事务操作之前的状态,我们只能对数据库中的数据进行修改然后执行回滚,因为数据库中的数据有数据库资源管理器进行强大的管理。
当我们使用IdbTransaction进行事务处理时其实是获取对远程事务处理的一个引用,比如SqlTransaction对象他就是逻辑上的事务资源管理器,当我们使用TransactionScope进行事务范围操作时,SqlServer数据提供程序能进行自动的事务提升并且进行事务资源登记,在最后能很好的进行二阶段提交协议进行数据的最终提交。
 
事务性资源管理器分类:
.NET事务模型提供了几个接口方便我们实现自定义的资源管理器,我们可以通过继承这些接口实现支持单阶段、两阶段提交协议的资源管理器。
 
1.    IenlistmentNotification接口:支持两阶段提交协议的资源管理器实现接口。
(官方解释:描述资源管理器为了在登记参与时为事务管理器提供两阶段提交通知回调而应该实现的接口。)
 
2.    IsinglePhaseNotification接口:支持单阶段协议的资源管理器实现接口。
(官方解释:描述支持单阶段提交优化以参与事务的资源对象。)
 
3. IpromotableSinglePhaseNotification接口:支持可提升的单阶段提交协议的资源管理器实现接口。(官方解释:描述作为资源管理器内部非分布式事务的提交委托的对象。)这个对象继承自ItransactionPromoter接口,该接口需要自动提升为由MSDTC管理的资源管理器使用的。
 
实现 System.Transactions.IenlistmentNotification 接口,自定义两阶段提交协议的资源管理器
下面我们通过实现IenlistmentNotification接口来开发一个简单的资源管理器。
代码1:资源管理器
 

 
 
  1. public class IEnlistmentNotificationDemo<T, Xcopy> : IEnlistmentNotification  
  2.         where T : new()  
  3.         where Xcopy : class 
  4.     {  
  5.         T _commitfrontvalue;  
  6.         T _rollbackfrontvalue = new T();  
  7.         Xcopy copy;  
  8.         public IEnlistmentNotificationDemo(T t, Xcopy icopy)  
  9.         {  
  10.             (icopy as IResourceCopy<T>).Copy(_rollbackfrontvalue, t);  
  11.             _commitfrontvalue = t;//保持对资源修改的引用  
  12.             copy = icopy;  
  13.         }  
  14.  
  15.         #region IEnlistmentNotification 成员  
  16.         public void Prepare(PreparingEnlistment preparingEnlistment)  
  17.         {  
  18.             //两阶段提交协议中的准备阶段  
  19.             Console.WriteLine("准备提交");  
  20.             ConsoleKeyInfo key = Console.ReadKey();  
  21.             if (key.KeyChar == 'Y' || key.KeyChar == 'y')  
  22.             {  
  23.                 preparingEnlistment.Prepared();  //投票提交事务  
  24.             }  
  25.             else if (key.KeyChar == 'N' || key.KeyChar == 'n')  
  26.             {  
  27.                 Console.WriteLine("\n由我投票整个事务回滚:" + _rollbackfrontvalue);  
  28.                 (copy as IResourceCopy<T>).Copy(_commitfrontvalue, _rollbackfrontvalue);//回滚事务自愿  
  29.                 preparingEnlistment.ForceRollback();//投票回滚事务,资源管理发生错误时因该将其自动回复数据,  
  30.                 //因为事务管理不会通知发生ForceRollback()方法的管理器。  
  31.             }  
  32.         }  
  33.         public void Commit(Enlistment enlistment)  
  34.         {  
  35.             //尝试提交  
  36.             Console.WriteLine("事务尝试提交");  
  37.             enlistment.Done();//通知事务管理器,参与者已完成提交工作。  
  38.         }  
  39.         public void Rollback(Enlistment enlistment)  
  40.         {  
  41.             //事务激活阶段处理错误,执行回滚  
  42.             Console.WriteLine("操作失败,回滚" + _rollbackfrontvalue);  
  43.             (copy as IResourceCopy<T>).Copy(_commitfrontvalue, _rollbackfrontvalue);//回滚事务自愿  
  44.             enlistment.Done();  
  45.         }  
  46.         public void InDoubt(Enlistment enlistment)  
  47.         {  
  48.             //与其他的资源管理器失去联系  
  49.             Console.WriteLine("与其他的资源管理器失去联系,通常记录日志");  
  50.             enlistment.Done();  
  51.         }  
  52.         #endregion  
  53.     }  
 
该类是支持两阶段提交协议的资源管理器,由于不同的资源面临这不同的数据复制操作。当我们进行初始数据备份的时候需要对不同的数据类型进行不同的数据复制操作,所以我们需要一个规范来进行约定。
代码2:资源复制接口
 

 
 
  1. /// <summary>  
  2.     /// 事务性资源管理器中的资源拷贝,不同的资源类型存在多种拷贝形式。  
  3.     /// </summary>  
  4.     public interface IResourceCopy<T>  
  5.     {  
  6.         void Copy(T t1, T t2);  
  7. }  
 
代码3:实现StringBuilder类型的数据复制
 

 
 
  1. /// <summary>  
  2.     /// StringBuilder类型的数据Copy对象,需要实现IResourceCopy泛型接口。  
  3.     /// </summary>  
  4.     public class StringBuilderCopy : IResourceCopy<StringBuilder>  
  5.     {  
  6.  
  7.         #region IResourceCopy<StringBuilder> 成员  
  8.  
  9.         public void Copy(StringBuilder t1, StringBuilder t2)  
  10.         {  
  11.             t1.Remove(0, t1.Length);  
  12.             t1.Append(t2.ToString());  
  13.         }  
  14.  
  15.         #endregion  
  16. }  
 
这样我们就有了对于StringBuilder类型的数据复制操作类。

代码4:将自定义的资源管理器参与到事务处理中

 


 
 
  1. /// <summary>  
  2.     /// 事务范围内的登记资源管理对象的状态  
  3.     /// </summary>  
  4.     public class EnlistmentDemo  
  5.     {  
  6.         [MethodImpl(MethodImplOptions.Synchronized)]//锁住当前方法,避免多线程访问破坏事务资源的一致性。  
  7.         public void Start()  
  8.         {  
  9.             //易失性资源  
  10.             StringBuilder stringvalues = new StringBuilder("123");  
  11.             StringBuilder stringvalues2 = new StringBuilder("456");  
  12.             StringBuilder stringvalues3 = new StringBuilder("789");  
  13.             StringBuilder stringvalues4 = new StringBuilder("101112");  
  14.             //使用资源管理器进行管理  
  15.             IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource =  
  16.                 new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues, new StringBuilderCopy());  
  17.             IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource2 =  
  18.                  new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues2, new StringBuilderCopy());  
  19.             IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource3 =  
  20.                 new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues3, new StringBuilderCopy());  
  21.             IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy> resource4 =  
  22.                  new IEnlistmentNotificationDemo<StringBuilder, StringBuilderCopy>(stringvalues4, new StringBuilderCopy());  
  23.             try 
  24.             {  
  25.                 using (TransactionScope transcope = new TransactionScope())  
  26.                 {  
  27.                     Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(commitran_TransactionCompleted);  
  28.                     //登记事务资源管理器,在开始一切事务性操作之前必须先登记资源管理器  
  29.                     Transaction.Current.EnlistVolatile(resource, EnlistmentOptions.None);  
  30.                     Transaction.Current.EnlistVolatile(resource2, EnlistmentOptions.None);  
  31.                     Transaction.Current.EnlistVolatile(resource3, EnlistmentOptions.None);  
  32.                     Transaction.Current.EnlistVolatile(resource4, EnlistmentOptions.None);  
  33.                     //开始事务性操作,该阶段属于事务的激活阶段。  
  34.                     stringvalues.Append("456");  
  35.                     stringvalues2.Append("789");  
  36.                     stringvalues3.Append("101112");  
  37.                     stringvalues4.Append("131415");  
  38.                     transcope.Complete();//开始两阶段提交,该阶段属于事务的准备阶段。  
  39.                 }  
  40.             }  
  41.             catch { Console.WriteLine("事务执行出错,执行回滚"); }  
  42.  
  43.             //查看被事务性操作后的值  
  44.             Console.WriteLine("事务完成后的结果值:");  
  45.             Console.WriteLine(stringvalues + "|" + stringvalues2 + "|" + stringvalues3 + "|" + stringvalues4);  
  46.         }  
  47.  
  48.         //事务结束时触发的事件方法,可以捕获事务执行结果。  
  49.         void commitran_TransactionCompleted(object sender, TransactionEventArgs e)  
  50.         {  
  51.             Console.WriteLine("transaction completed:");  
  52.             Console.WriteLine("ID:             {0}", e.Transaction.TransactionInformation.LocalIdentifier);  
  53.             Console.WriteLine("Distributed ID: {0}", e.Transaction.TransactionInformation.DistributedIdentifier);  
  54.             Console.WriteLine("Status:         {0}", e.Transaction.TransactionInformation.Status);  
  55.             Console.WriteLine("IsolationLevel: {0}", e.Transaction.IsolationLevel);  
  56.         }  
  57.     }  
 
我们来看一下效果:

图1:COMMIT事务

图2:ROLLBACK事务

这样我们就能很好的将自定义的资源管理器参与到事务处理当中来,对于分布式的事务处理其实也是一样的,在事务的操作范围内首先进行资源管理器的登记才能使用。[王清培版权所有,转载请给出署名]

 事务处理是一个庞大的领域,我这里只是一个小小的应用。希望大家能用的着。





 本文转自 王清培 51CTO博客,原文链接:http://blog.51cto.com/wangqingpei557/756442 ,如需转载请自行联系原作者

相关文章
|
开发框架 JSON .NET
ASP.NET Core 自定义配置警告信息
自定义配置警告信息需要在 startup 类中的 ConfigureService 方法中进行配置示例: // 注册 控制器服务 services.AddControllers(configure: setup => { setup.ReturnHttpNotAcceptable = true; ...
89 0
|
XML 存储 JSON
使用自定义XML配置文件在.NET桌面程序中保存设置
本文将详细介绍如何在.NET桌面程序中使用自定义的XML配置文件来保存和读取设置。除了XML之外,我们还将探讨其他常见的配置文件格式,如JSON、INI和YAML,以及它们的优缺点和相关的NuGet类库。最后,我们将重点介绍我们为何选择XML作为配置文件格式,并展示一个实用的示例。
139 0
|
2月前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
4月前
|
开发框架 .NET Docker
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
|
5月前
|
安全 数据库连接 Python
告别繁琐!自定义Python上下文管理器,让你的资源管理变得如此简单
【7月更文挑战第4天】在Python中,上下文管理器通过`with`语句简化资源管理,确保资源的自动获取与释放,增强程序稳定性。自定义上下文管理器依靠`__enter__`和`__exit__`方法,例如,`CustomFileManager`类展示了如何记录文件操作。自定义管理器能简化代码、保证资源安全释放和提供定制逻辑,从而提升代码的健壮性和可维护性。它是处理文件、连接等资源管理的强大工具。
48 2
|
5月前
|
数据库连接 数据库 Python
Python 大神教你一招鲜:自定义上下文管理器,让资源管理变得如此轻松惬意🍵
【7月更文挑战第3天】在Python中,自定义上下文管理器是高效资源管理的关键,它们确保了如文件和数据库连接等资源的正确打开和关闭。通过`__enter__`和`__exit__`方法,可以创建简洁的代码,避免冗余的异常处理。例如,自定义的`DatabaseConnectionManager`和`FileManager`类使得数据库操作和文件读取更整洁,使用`with`语句自动处理资源生命周期,提升代码可读性和可靠性。
52 0
|
6月前
|
安全 程序员 Shell
老程序员分享:NSIS自定义界面,下载并安装Net.Framework4.8
老程序员分享:NSIS自定义界面,下载并安装Net.Framework4.8
|
6月前
|
存储 分布式计算 大数据
MaxCompute操作报错合集之自定义udf的函数,引用了import net.sourceforge.pinyin4j.PinyinHelper;但是上传资源后,出现报错,是什么原因
MaxCompute是阿里云提供的大规模离线数据处理服务,用于大数据分析、挖掘和报表生成等场景。在使用MaxCompute进行数据处理时,可能会遇到各种操作报错。以下是一些常见的MaxCompute操作报错及其可能的原因与解决措施的合集。
125 0
|
7月前
|
XML API 数据库
七天.NET 8操作SQLite入门到实战 - 第六天后端班级管理相关接口完善和Swagger自定义配置
七天.NET 8操作SQLite入门到实战 - 第六天后端班级管理相关接口完善和Swagger自定义配置
133 0
|
Windows
基于.Net Core实现自定义皮肤WidForm窗口
基于.Net Core实现自定义皮肤WidForm窗口
144 0