一、引言
至此还没提及到WCF,总感觉自己欠了什么一样的,所以今天迫不及待地来更新了,因为后面还有好几个系列准备些,还有很多东西需要学习总结的。今天就来介绍下WCF对事务的支持。
二、WCF事务详解
2.1 事务概念与属性
首先,大家在学习数据库的时候就已经接触到事务这个概念了。所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单元。例如,银行转账功能,这个功能涉及两个逻辑操作
- 从一个账户A中扣钱
- 另一个账户B增加对应的钱。
现实生活中,这两个操作需要要么都执行,要么都不执行。所以在实现转账功能时,这两个操作就可以作为一个事务来进行提交,这样才能够保证转账功能的正确执行。
上面通过银行转账的例子来解释了事务的概念了,也可以说非常容易理解。然后在数据库的相关书籍里面都会介绍事务的特性。一个逻辑工作单元要成为事务,必须满足四个特性,这四个特性包括原子性、一致性、隔离性和持久性。这四个特性也简称为ACID(ACID是四个特性英文单词首字母的缩写)。
- 原子性。此属性可确保特定事务下完成的所有更新都已提交并保持持久,或所有这些更新都已中止并回滚到其先前状态。
- 一致性。此属性可保证某一事务下所做的更改表示从一种一致状态转换到另一种一致状态。例如,将钱从支票帐户转移到存款帐户的事务并不改变整个银行帐户中的钱的总额。
- 隔离。此属性可防止事务遵循属于其他并发事务的未提交的更改。隔离在确保一种事务不能对另一事务的执行产生意外的影响的同时,还提供一个抽象的并发。
- 持久性。这意味着一旦提交对托管资源(如数据库记录)的更新,即使出现失败这些更新也会保持持久。
2.2 事务协议
WCF支持分布式事务,也就是说WCF中的事务可以跨越服务边界、进程、机器和网络,在多个客户端和服务之间存在。即WCF中事务可以被传播的。既然WCF支持事务,则自然就有对应传输事务信息的相关协议。所以也就有了事务协议。
WCF使用不同的事务协议来控制事务的执行范围,事务协议是为了实现分布式环境中事务的传播。WCF支持以下三种事务协议:
- 轻量级协议(Lightweight Protocol):该协议仅用于管理本地环境中的事务,即处于同一应用程序域中的事务。它无法跨越应用程序域的边界传播事务,则更不用说跨越进程和机器边界了。所以Lightweight Protocol只适用于某个服务的内部或外部。但相对于其他协议来说,轻量级协议的性能是最好的,这应该是显然的,不能跨越进程和机器边界,则就不存在网络传输。
- OleTx协议:该协议用于跨应用程序域、进程和机器边界传播事务。协议采用远程过程调用(RPC),并采用Windows专用的二进制格式。但该协议无法穿越防火墙或与非Windows方协作。所以OleTx协议多用于Windows体系下的内网环境(即Intranet环境)。
- WS-Atomic(WS原子性,WSAT)事务协议:该协议与OleTx协议类似,同样允许事务穿越应用程序域、进程和机器边界传播事务。但不同于OleTx协议的是,WSAT协议基于一种行业标准,它使用HTTP协议,并编码形式为文本格式,因而可以穿越防火墙。虽然可以在内网中使用WSAT协议,但它主要还是用于Internet环境。
因为轻量级协议不能跨越服务边界传播事务,所有没有绑定支持轻量级协议。WCF预定义的绑定中实现了标准的WS-Atomic 协议和Microsoft专有的OleTx协议,我们可以通过编程或配置文件来设置事务协议。具体设置方法如下所示:
1 <bindings> 2 <netTcpBinding> 3 <!--通过transactionProtocol属性来设置事务协议--> 4 <binding name="transactionTCP" transactionFlow="true" transactionProtocol="WSAtomicTransactionOctober2004"/> 5 </netTcpBinding> 6 </bindings> 7 // 通过编程设置 8 NetTcpBinding tcpBinding = new NetTcpBinding(); 9 // 注意: 事务协议的配置只有在事务传播的情况下才有意义 10 tcpBinding.TransactionFlow = true; 11 tcpBinding.TransactionProtocol = TransactionProtocol.WSAtomicTransactionOctober2004;
这里需要注意,事务协议的配置只有在允许事务传播的情况下才有意义。并且NetTcpBinding和NetNamedPipeBinding都提供了TransactionProtocol属性。由于TCP和IPC绑定只能在内网使用,将它们设置为WSAT协议并无实际意义,对于WS绑定(如WSHttpBinding、WSDualHttpBinding和WSFederationHttpBinding)并没有TransactionProtocol属性,它们设计的目的在于当涉及多个使用WAST协议的事务管理器时,能够跨越Internet。但如果只有一个事务协调器,OleTx协议将是默认的协议,不必也不能为它配置一个特殊的协议。
2.3 事务管理器
分布式事务的实现要依靠第三方事务管理器来实现。它负责管理一个个事务的执行情况,最后根据全部事务的执行结果,决定提交或回滚整个事务。WCF提供了三个不同的事务管理器,它们分别是轻量级事务管理器(LTM)、核心事务管理器(KTM)和分布式事务协调器(DTC)。WCF根据平台使用的公共,应用程序的事务执行的任务、调用的服务以及所消耗的资源分配合适的事务管理器。通过自动地分配事务管理器,WCF将事务管理从服务代码和用到的事务协议中解耦出来,开发者不必为事务管理器而苦恼。下面分别介绍下这三种事务管理器。
- LTM:它只管理本地事务,即在一个单独应用程序域内的事务。LTM只能管理在一个单独服务内的事务,该服务不能将事务传递给其他服务。LTM在所有的事务管理器中,性能最好。
- KTM:与LTM一样,KTM管理的事务只能引入一个服务,并且该服务不得向其他服务传播事务。
- DTC:DTC可以管理跨越任意执行边界的事务,从本地跨越所有的边界,如进程、机器或站点的边界。DTC既可以使用OleTx协议,也可以使用WSAT协议。DTC与WCF紧密的集成一起,它是每个运行WCF的机器上默认可用的系统服务,DTC可以创建新的事务、跨机器传播事务,手机之一管理器的投票并通知资源管理器进行回滚或提交。
2.4 服务支持的4种事务模式
事务使用哪个事务由绑定的事务流属性(TransactionFlow属性)、操作契约中的事务流选项(TransactionFlowOption) 以及操作行为特性中的事务范围属性(TransactionScopeRequired)共同决定。TransactionFlow属性有2个值,true 或false,TransactionFlowOption有三个值,NotAllowed、Allowed和Mandatory,TransactionScopeRequired有两个值,true或false。所以一共有12种(2*3*2)可能的配置设置。在这些配置设置中,有4种不满足要求的,例如在绑定中设置TransactionFlow属性为false,却设置TransactionFlowOption为Mandatory。下图列出了剩下的8种情况:
上图中的8中排列组合实际最终只产生了四种事务传播模式,这4种传播模式为:Client/Service、Client、Service和None。上图黑体字指出各种模式推荐的配置设置。在设计应用程序时,每种模式都有它自己的适用场景。对于除None模式的其他三种模式的推荐配置详细介绍如下所示:
- Client/Service:最常见的一种事务模型,通常由客户端或服务本身启用一个事务。设置步骤:
(1) 选择一个支持事务的Binding,设置 TransactionFlow = true。
(2) 设置 TransactionFlow(TransactionFlowOption.Allowed)。
(3) 设置 OperationBehavior(TransactionScopeRequired=true)。
- Client:强制服务必须参与事务,而且必须是客户端启用事务。设置步骤:
(1) 选择一个支持事务的Binding,设置 TransactionFlow = true。
(2) 设置 TransactionFlow(TransactionFlowOption.Mandatory)。
(3) 设置 OperationBehavior(TransactionScopeRequired=true)。
- Service:服务必须启用一个根事务,且不参与任何外部事务。设置步骤:
(1) 选择任何一种Binding,设置 TransactionFlow = false(默认)。
(2) 设置 TransactionFlow(TransactionFlowOption.NotAllowed)。
(3) 设置 OperationBehavior(TransactionScopeRequired=true)。
三、WCF事务服务的实现
上面内容对WCF中事务进行了一个详细的介绍,下面具体通过一个实例来说明WCF中如何实现对事务的支持。首先还是按照前面博文中介绍的步骤来实现该实例。
第一步:创建WCF契约和契约的实现,具体的实现代码如下所示:
namespace WCFContractAndService { // 服务契约 [ServiceContract(SessionMode= SessionMode.Required)] //[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = false)] public interface IOrderService { // 操作契约 [OperationContract] // 控制客户端的事务是否传播到服务 // TransactionFlow的值会包含在服务发布的元数据上 [TransactionFlow(TransactionFlowOption.NotAllowed)] List<Customer> GetCustomers(); [OperationContract] [TransactionFlow(TransactionFlowOption.NotAllowed)] List<Product> GetProducts(); [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] string PlaceOrder(Order order); [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] string AdjustInventory(int productId, int quantity); [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] string AdjustBalance(int customerId, decimal amount); } [DataContract] public class Customer { [DataMember] public int CustomerId { get; set; } [DataMember] public string CompanyName { get; set; } [DataMember] public