操作行为 之 事务
事务场景:
1. 长期事务:多步骤业务处理,时间跨度长,如几分钟、几天、上月,涉及多个组织或工作流;
2. 短期事务:简单业务处理,时间段,几秒内完成,外部以来很少;
短期事务ACID特性 :原子性、一致性、隔离性、持久性
WCF支持短期事务,利用两种架构来实现:
1. .Net Framewok 和 Windows Infrastructure 支持 Microsoft环境中运行的事务;
2. 利用跨平台的WS-* 标准。
事务化操作
声明操作行为: [OperationBehavior(TransactionScopeRequired=true)],表示将服务操作标识为事务化操作。
操作说明:
1. WCF在将控制权转换为服务方法前创建一个新事务;
2. 将执行线程纳入第一步创建的事务中;
可以显式或隐式的指定当操作完成时的提交事务的操作。 声明操作行为:[OperationBehavior(TransactionAutoComplete=true)]
TransactionAutoCompleted=true 表示若没有抛出任何错误,操作会隐式的认定执行完成;若抛出错误,操作会认定为未完成,针对事务资源的部分更新会被回滚。
TransactionAutoCompleted=false 表示必须在方法返回前显示的调用 OperationContext.Current.SetTransactionCompleted() ,若使用了显式方法,则必须在通信信道中使用基于会话的绑定,并使用[ServiceContract(SessionMode=SessionMode.Allowed)]使用服务契约支持会话。
代码示例:BankService 实现两个方法,GetBalance 不需要事务,Transfer 指定了事务,并隐式提交事务。通过输出事务的 LocalIdentifier和DistributedIdentifier来判断事务执行情况
public class BankService: IBankService { #region IBankService Members [OperationBehavior(TransactionScopeRequired = false)] public double GetBalance(string AccountName) { DBAccess dbAccess = new DBAccess(); double amount = dbAccess.GetBalance(AccountName); dbAccess.Audit(AccountName, "Query", amount); return amount; } [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void Transfer(string From, string To, double amount) { try { Withdraw(From, amount); Deposit(To, amount); } catch (Exception ex) { throw ex; } } #endregion private void Withdraw(string accountName, double amount) { DBAccess dbAccess = new DBAccess(); dbAccess.Withdraw(accountName, amount); dbAccess.Audit(accountName, "Withdraw", amount); } private void Deposit(string accountName, double amount) { DBAccess dbAccess = new DBAccess(); dbAccess.Deposit(accountName, amount); dbAccess.Audit(accountName, "Deposit", amount); } }
执行结果:
在执行过程中若本机没有打开MSDTC服务,则会出现错误,必须要进行配置。
结果分析:
1. Withdraw 执行中打开第一个数据库连接,当前存在一个上下文事务,并且属于本地事务;
2. Deposit 执行中打开第二个数据库连接,导致事务从本地事务升级到分布式事务;
导致事务升级的代码:
private void Withdraw(string accountName, double amount) { DBAccess dbAccess = new DBAccess();//创建SqlConnection dbAccess.Withdraw(accountName, amount); dbAccess.Audit(accountName, "Withdraw", amount); } private void Deposit(string accountName, double amount) { DBAccess dbAccess = new DBAccess();//创建SqlConnection dbAccess.Deposit(accountName, amount); dbAccess.Audit(accountName, "Deposit", amount); }
由于创建两个SqlConnection实例,导致分布式事务出现,可以对此进行优化,将dbAccess 实例作为全局变量,共用同一个SqlConnection实例。优化后结果如下:
操作之间的事务流转
背景:跨越服务边界的事务,如:用户下订单需要同时调用订单服务及客户信息服务,增加订单的同时增加客户信息。
在WCF中,服务边界之间的事务流转信息统称为事务流(transaction flow),要在服务边界之间定义事务流,需要以下5个操作:
1. (服务契约)SessionMode.Required,服务契约必须使用Session,只有这样客户端才能在参与事务的各个服务之间共享信息;
2. (操作行为)TransactionScopeRequired=true,操作行为必须在事务上下文内,若不存在上下文,则创建一个新事物;
3. (操作契约)TransactionFlowOption.Allowed,操作契约必须允许消息头中包含用于流转的事务信息;
4. (绑定)TransactionFlow=true,允许事务跨越多个服务调用, 绑定必须激活事务流,使得信道能在SOAP头中放置事务信息,绑定必须支持会话;
5. (客户端)TransactionScope,初始化事务的角色(通常是客户端)必须在调用服务操作时使用一个事务作用域,必须调用TransactionScope.Close()来提交修改;
暂无Demo