C#综合揭秘细说事务.docx
《C#综合揭秘细说事务.docx》由会员分享,可在线阅读,更多相关《C#综合揭秘细说事务.docx(33页珍藏版)》请在冰豆网上搜索。
C#综合揭秘细说事务
C#综合揭秘——细说事务
引言
其实事务在数据层、服务层、业务逻辑层多处地方都会使用到,在本篇文章将会为大家一一细说。
其中前面四节是事务的基础,后面的三节是事务的重点,对事务有基础的朋友可以跳过前面四节。
文章有错漏的地方欢迎各位点评。
目录
一、事务的定义
二、事务管理器
三、在ADO.NET中实现事务
四、隐式事务TransactionScope
五、在WCF中实现事务
六、嵌套式事务
七、异步事务
一、事务的定义
所谓事务,它是一个操作集合,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
典型的例子就像从网上银行系统的帐户A转帐到帐户B,它经过两个阶段:
1.从帐户A取出款项。
2.把款项放入帐户B中。
这两个过程要么同时成功,要么同时失败,这一系列的操作就被称为事务性(Transactional)操作。
在一个事务性操作的环境下,操作有着以下的4种特性,被称为ACID特性
原子性(Atomicity)
当事务结束,它对所有资源状态的改变都被视为一个操作,这些操作要不同时成功,要不同时失败
一致性(Consistency)
操作完成后,所有数据必须符合业务规则,否则事务必须中止
隔离性(Isolation)
事务以相互隔离的方式执行,事务以外的实体无法知道事务过程中的中间状态
持久性(Durable)
事务提交后,数据必须以一种持久性方式存储起来
二、事务管理器
在软件系统当中可以看到无论在数据库、Web服务、WCF、文件系统都存在着数据参与到事务运作当中,我们把管理这些数据的工具称为资源管理器RM(ResourcesManager)。
而事务管理器TM(TransactionManager)就是协调多个资源管理器的工作,保证数据完整性的工具。
由上图可以看到事务的管理流程,系统通知事务管理器TM来启动事务,事务管理器TM控制向多个资源管理器RM并协调RM之间的事务操作。
图中存在两个持久化RM,分别管理数据库和文件系统,这些事务操作要不同时成功,要不同时失败。
事务管理器一般分为三类:
轻量级事务管理器(LTM)、核心事务管理器(KTM)、分布式事务协调器(DTC)
1.轻量级事务管理器(LTM)
它是包括在System.Transactions命名空间内的一个事务管理框架,它只能管理单个应用程序域内的事务。
LTM可以管理多个易变的RM,但只能管理一个持久化RM。
若事务试图加入第二个持久化RM,那轻量级事务管理器LTM将提升级别。
LTM是性能最高的事务管理器,在可选择的情况下应该尽可能地使用LTM事务管理器。
这里易变RM是指它参与会引发“未确定状态”的2PC事务时候,不需要恢复服务,更多时候,易变RM的数据只存储在内存当中。
而持久化RM是指它参与会引发“未确定状态”的2PC事务时候,它需要恢复服务,持久化RM管理的数据是在于硬盘当中。
所以,参与2PC事务的的持久RM必须有新旧两个版本,如果事务引发“未确定状态”的时候,那么它就会联系持久化RM,恢复到其中一个版本。
2PC说明(:
//
2PC是2PhaseCommit的缩写,代表事务的2阶段提交验证算法:
在数据提交时,第一阶段:
应用程序记录每个数据源并执行更新请求,TM通知每个RM来执行分布式事务,然后每个RM都对数据执行本地的事务,在事务将提交前,TM会与各个RM进行信息交换,以获知更新是否成功。
第二阶段,如果其中任何一个RM表示更新失败,TM就会通知所有的RM事务操作失败,实现数据回滚。
如果所有RM的操作都成功,那么整个TM事务就宣告成功。
2.核心事务管理器(KTM)
KTM是用于WindowsVista和WindowsServer2008系统中的轻量级事务管理器,与LTM相像,它可以管理多个易变的RM,但只能管理一个持久化RM。
3.分布式事务协调器(DTC)
分布式事务协调器DTC(DistributedTransactionCoordinator)能管理多个持久化RM中的事务,事务可以跨越应用程序域、进程、硬件、域等所有的边界。
在WindowsServer2008当中,DTC支持OleDB、XA、WS-AtomicTransaction、WSCoordination、WS-BusinessActivity等多个协议。
由于分布式事务需要在多个参与方之间实现多次通讯,所以是一种巨大的开销,因此,在可以使用LTM和KTM的时候,应该尽量避免使用DTC。
在上面图片中的事务同时启动了两个RM分别处理数据库数据与文件数据,当中启动的就是DTC分布式事务。
4.事务类System.Transactioins.Transaction
Transaction是由Framework2.0就开始引入,用于显示管理事务的一个类。
通过Transaction可以直接管理事务的中止、释放,也可以获取、克隆当前的环境事务类。
Transaction的公用属性
其中Transaction.Current比较常用,它可以指向一个当前运行环境中的事务,如果环境事务不存在,系统将返回一个null
Transactiontransaction=Transaction.Current;
属性
说明
Current
获取或设置环境事务。
IsolationLevel
获取事务的隔离级别。
TransactionInformation
检索有关某个事务的附加信息。
Transaction的常用公用方法
其中Rollback、Dispose方法可以控制事务中止、释放,而Clone、DependentClone方法在多线程操作中经常用到,在 “异步事务” 一节中将详细说明
方法
说明
Rollback
中止事务、回滚。
Dispose
释放事务对象。
Clone
创建事务克隆
DependentClone
创建事务的依赖克隆。
Transaction的事件
在事务完成后,会触发TransactionCompleted事件,开发人员可以在此事件的过程监测其状态
事件
说明
TransactionCompleted
在事务完成后执行
5.事务状态TransactionInformation
上面讲解过事务分为本地事务与分布式事务,而Transaction类的TransactionInformation是事务状态的记录,它可以跟踪事务动作,分辨事务现处的状态,记录本地事务与分布式事务的Guid。
TransactionInformation有两个重要成员
1publicclassTransactionInformation
2{
3//返回分布式事务标识符
4publicGuidDistributedIdentifier
5{get;}
6
7//返回本地事务标识符
8publicstringLocalIdentifier
9{get;}
10}
LocalIndentifier是本地事务的标识符,它可以获取本地事务管理器(LTM)的ID,并且注意只要事务存在,它的值就永远不会是null。
它包含两个部分,一个是LTM的Guid,它是应用程序中的唯一值,代表了现存应用程序域分配的LTM。
另一部分是一个可变量,代表了当时该应用程序域中的事务数量。
例如:
3427dec9-4abc-34cc-9edf-30ad835c33k3:
3
其中3427dec9-4abc-34cc-9edf-30ad835c33k3是此本地事务管理器的Guid,在事务启动后,此值都是不变的,而“3”代表此刻该应用程序域中存在“3”个本地事务。
DistributedIndentifier是分布式事务的标识符,在普通情况下DistributedIndentifier的值都为Guid.Empty。
但当LTM或KTM事务被提升到分布式事务时,DistributedIndentifier就会产生。
最重的是,在同一个分布式事务管理器当中,即使事务跨越服务边界,分布式ID都是一致的。
DistributedIndentifier是分布式事务的唯一标识符,它的使用方法在后面 “事务的传播” 一节将详细介绍。
在TransactionManager类中,还提供了一个事件DistributedTransactionStarted专门用于测试分布式事务的变化。
1classProgram
2{
3staticvoidMain(string[]args)
4{
5using(TransactionScopescope=newTransactionScope())
6{
7TransactionManager.DistributedTransactionStarted+=OnDistributedTransactionStarted;
8............
9scope.Complete();
10}
11Console.ReadKey();
12}
13
14//当执行分布式事务是就会启动此方法显示事务信息
15staticvoidOnDistributedTransactionStarted(objectsender,TransactionEventArgsargs)
16{
17Transactiontransaction=args.Transaction;
18Console.WriteLine("DistributedTransactionStarted!
\nDistributedIndentifier:
"
19+transaction.TransactionInformation.DistributedIdentifier);
20}
21}
基础知识就先讲到这里,下面开始介绍一下事务的具体用法。
三、在ADO.NET中实现事务
1.ADO.NET事务的主要成员
需要使用事务管理的情况很多,在数据层使用得特别广泛,几乎每一个系统的数据层都会实现事务。
数据层的事务都是继承自DBTransaction,派生自IDbTransaction的。
下面介绍一下IDbTransaction的基本成员:
1publicinterfaceIDbTransaction:
IDisposable
2{
3IDbConnectionConnection{get;}//返回Connection对象
4IsolationLevelIsolationLevel{get;}
5voidCommit();//数据提交,把所有改变数据保存到持久化数据库
6voidRollback();//数据回滚,把所有数据恢复原值
7}
其中Connection属性是返回初始化此事务时所引用的连接对象的。
Commit()方法应该在完成所有数据操作后才调用,调用该方法后,已经改变的数据将会保存到持久化数据库当中。
而Rollback()是出现错误时调用的,调用后数据将返回初始值。
IsolationLevel是指定遇到其它并行事务时的处理方式。
ADO.NET当中有多个子类都继续自DBTransaction,其中SqlTransaction是比较常用的,SqlTransaction中还定义了一个Save()方法,这个方法允许开发人员把失败的事务回滚到上一个保存点而不回滚整个事务。
而在DataContext类里面,Transaction属性会返回DBTransaction对象。
2.开发实例
在传统的ADO.NET中使用事务,方法如下:
1privatestaticvoidExecute(stringconnectionString)
2{
3using(SqlConnectionconnection=newSqlConnection(connectionString))
4{
5connection.Open();
6
7SqlCommandcommand=connection.CreateCommand();
8SqlTransactiontransaction;
9
10//启动事务
11transaction=connection.BeginTransaction("SampleTransaction");
12
13//设定SqlCommand的事务和连接对象
14command.Connection=connection;
15command.Transaction=transaction;
16
17try
18{
19command.CommandText="Insertinto......";
20command.ExecuteNonQuery();
21
22//完成提交
23transaction.Commit();
24......
25}
26catch(Exceptionex)
27{
28//数据回滚
29transaction.Rollback();
30.....
31}
32}
33}
在DataContext中使用事务,方法极其相似,不同的是SqlCommand中事务为SqlTransaction,在DataContext中事务为DbTransaction
1using(MyDataContextcontext=newMyDataContext())
2{
3try
4{
5context.Connection.Open();
6context.Transaction=context.Connection.BeginTransaction();
7//更新数据
8.........
9context.SubmitChanges();
10//事务提交
11context.Transaction.Commit();
12}
13catch(Excetionex)
14{
15//数据回滚
16context.Transaction.Rollback();
17//错误处理
18.........
19}
20}
四、隐式事务TransactionScope
1.TransactionScope的概念
TransactionScope存在于System.Transactions 命名空间中,它是从Framework2.0开始引入的一个事务管理类,它也是微软推荐使用的一个事务管理类。
在TransactionScope的构造函数中会自动创建了一个新的LTM(轻量级事务管理器),并通过Transaction.Current隐式把它设置为环境事务。
在使用隐式事务时,事务完成前程序应该调用TransactionScope的Complete()方法,把事务提交,最后利用Dispose()释放事务对象。
若执行期间出现错误,事务将自动回滚。
1publicclassTransactionScope:
IDisposable
2{
3//多个构造函数
4publicTransactionScope();
5publicTransactionScope(Transaction)
6publicTransactionScope(TransactionScopeOption)
7......
8publicvoidComplete();//提交事务
9publicvoidDispose();//释放事务对象
10}
11
12//调用方式
13using(TransactionScopescope=newTransactionScope())
14{
15//执行事务型工作
16............
17scope.Complete();
18}
2.TransactionScope的构造函数TransactionScope(transactionScopeOption)
TransactionScopeOption 是枚举的一个实例,它主要用于TransactionScope的构造函数内,定义事务生成的状态要求。
在MSDN里面可以找到它的定义:
:
//
成员名称
说明
Required
该范围需要一个事务。
如果已经存在环境事务,则使用该环境事务。
否则,在进入范围之前创建新的事务。
这是默认值。
RequiresNew
总是为该范围创建新事务。
Suppress
环境事务上下文在创建范围时被取消。
范围中的所有操作都在无环境事务上下文的情况下完成。
这里Suppress有点特别,当使用Suppress范围内,所有的操作都将在无事务的上下文中执行,即当中的程序不再受到事务的保护,这大多数在嵌套式事务中使用。
1voidDoWork()
2{
3using(TransactionScopescope=newTransactionScope())
4{
5//在事务环境中执行操作
6......
7NoTransaction();
8scope.Complete();
9}
10}
11
12voidNoTransaction()
13{
14//在无事务环境中执行操作
15using(TransactionScopescope=newTransactionScope(TransactionScopeOption.Suppress))
16{
17......
18}
19}
3.应用实例,在EntityFramework中使用TransactionScope
在ObjectContext.SaveChanges方法中已经包含了事务的保护,自从微软开源了EntityFramework后,在下才在CodePlex了解到其实现方式。
一般在单体个实体的插入、更新、删除等简单操作中,不需要额外加载事务。
除非在关联表操作,多次使用ObjectContext.SaveChanges方法时,此时则需要使用事务保存数据操作的一致性。
因为在SaveChanges中已经包含了事务,此时所实现的事务操作已经第六节所说到的嵌套式事务。
1publicintUpdateOrder(Orderorder)
2{
3using(BusinessEntitiescontext=newBusinessEntities())
4{
5EntityKeyentityKey=new
6EntityKey("BasicArchitectureEntities.Order","Id",order.Id);
7varobjResult=context.GetObjectByKey(entityKey);
8
9using(TransactionScopescope=newTransactionScope())
10{
11if(objResult!
=null)
12context.ApplyCurrentValues("Order",order);
13foreach(OrderItemiteminorder.OrderItem)
14UpdateOrderItem(item);
15returncontext.SaveChanges();
16}
17}
18}
19
20publicintUpdateOrderItem(OrderItemitem)
21{
22using(BusinessEntitiescontext=newBusinessEntities())
23{
24EntityKeyentityKey=new
25EntityKey("BasicArchitectureEntities.OrderItem","Id",item.Id);
26varobjResult=context.GetObjectByKey(entityKey);
27
28//更新实体属性
29if(objResult!
=null)
30context.ApplyCurrentValues("OrderItem",item);
31returncontext.SaveChanges();
32}
33}
五、在WCF中实现事务
1.WCF服务中事务的边界
把WCF的事务边界独立成一节,是想大家注意这一点,WCF服务中,事务是以方法为边界的,每个WCF服务的方法可以有独立事务的执行模式。
而事务可以在多个服务中传播,也可以在服务端与客户端之间传播,介时事务管理器的级数将会晋升。
2.简单的事务使用方式
TransactionScopeRequired与TransactionAutoComplete是WCF事务的基本元素。
当TransactionScopeRequired等于true时,代表在此WCF服务的方法中启动事务。
反之,当此值为false时代表此方法不执行事务。
当TransactionAutoComplete等于true时,代表该方法使用隐式事务,这也是微软推荐使用的方法。
即当该方法在运行过程中没有抛出Exception,操作就默认为完成,事务将自动提交。
如果期间出现任何异常,事务就会自动回滚。
如果TransactionAutoComplete等于false时,该方法即为显式事务,即需要在方法完成时利用OperationContext.Current.SetTransactionComplete()显式提交事务。
1[ServiceContract(SessionMode=SessionMode.Required)]
2publicinterfaceIService
3{
4[OperationContract]
5voidMethod1();
6
7[OperationContract]
8voidMethod2();
9}
10
11publicclassService:
IService
12{
13//隐式事务
14[OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete