【WCF--初入江湖】07 分布式事务
07 分布式事务
一、前言
二、事务
三、TransactionFlow 特性
[TransactionFlow(TransactionFlowOption.Mandatory)]
int serviceMethod(int value)
{ }
四、[ServiceBehavior]中的事务属性
【1】TransactionAutoCompleteOnSessionClose
如果想要确保关闭会话后待处理的消息仍然可以完成,应该使用该属性。
根据其属性值,事务将会在会话关闭后提交或回滚。
TransactionIsolationLevel用于指示事务隔离级别。
IsolationLevel枚举如下:
(1)ReadUncommitted:读取未提交数据,该方式在读取数据时保持共享锁定以避免读取已修改的数据,但在事务结束前可以更改这些数据,这导致非可重复读取或幻读。
(2)ReadCommitted:读取提交数据, 发出共享锁定并允许非独占方式的锁定。该方式与读取未提交数据相相似,这种方式看似和读取未提交数据相似,但有一个区别,事务的只读锁在移到下一行的时候,会解锁,而写入锁却只有在事务完成或者被中止后才解锁,事务要等待所有写入锁解锁。
(3)RepeatableRead:可重复性读取,与读取提交数据相似,在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。防止非可重复读取,但幻读行仍有可能发生。该方式是只读锁也要等到事务结束或者中止才解除
(4)Serializable:在完成事务前防止更新或插入。
ReadUncommitted是最低的隔离级别
Serializable是最高的隔离级别
---------------
理解事务隔离级别:
用于指示事务的超时时间,默认为TimeSpan.Zero,表示不会受超时时间的限制。
示例:
ServiceBehavior(TransactionAutoCompleteOnSessionClose=true,
TransactionIsolationLevel=IsolationLevel.ReadCommitted,
TransactionTimeout="00:00:30")]
public Class ServiceClass:IServiceClass
{ }
五、实例
设置步骤:
Create database WCFTransactionDb;
use WCFTransactionDb;
Create table Account
(
Id int not null primary key, --账号
Balance float --余额
)
insert into Account values(, 3000.0);
insert into Account values(, 2000.0);
更新和查看:
update Account set Balance=Balance-200
where Id=1000; update Account set Balance=Balance+200
where Id=1001; SELECT TOP 1000 [Id]
,[Balance]
FROM [WCFTransactionDb].[dbo].[Account]
【ServiceBehavior】的有关属性 及其要实现事务的方法添加[OperationBehavior(TransactionScopeRequired = true ]
IService1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace Keasy5.WCF.Transaction.WCFService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory/*强制事务必须成为流*/)]
void OutMoney(int id, double money); [OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory/*强制事务必须成为流*/)]
void IntoMoney(int id, double money);
}
}
Service1.cs
namespace Keasy5.WCF.Transaction.WCFService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single/*对象用于所有传入呼叫,并且在调用后不回收。如果服务对象不存在,则创建一个。*/
,ReleaseServiceInstanceOnTransactionComplete = false /*获取或设置一个值,该值指定在完成当前事务后是否释放服务对象*/
)]
public class Service1 : IService1
{
[OperationBehavior(TransactionScopeRequired = true /*该值指示方法在执行时是否需要事务范围*/)]
public void OutMoney(double money)
{ } [OperationBehavior(TransactionScopeRequired = true /*该值指示方法在执行时是否需要事务范围*/)]
public void IntoMoney(double money)
{ }
}
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace Keasy5.WCF.Transaction.WCFService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single/*对象用于所有传入呼叫,并且在调用后不回收。如果服务对象不存在,则创建一个。*/
,ReleaseServiceInstanceOnTransactionComplete = false /*获取或设置一个值,该值指定在完成当前事务后是否释放服务对象*/
)]
public class Service1 : IService1
{
private const string ConnectString = "Data Source=.;Initial Catalog=WCFTransactionDb;Integrated Security=True"; [OperationBehavior(TransactionScopeRequired = true /*该值指示方法在执行时是否需要事务范围*/)]
public void OutMoney(int id, double money)
{
ChangeMoneyToDb(id, money, false);
} [OperationBehavior(TransactionScopeRequired = true /*该值指示方法在执行时是否需要事务范围*/)]
public void IntoMoney(int id, double money)
{
ChangeMoneyToDb(id, money, true);
} /// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="money"></param>
/// <param name="inMoney">inMoney=true表示转入,否则表示转出</param>
private static void ChangeMoneyToDb(int id, double money, bool inMoney)
{
using (SqlConnection sqlConnection = new SqlConnection(ConnectString))
{
sqlConnection.Open(); using (SqlCommand command = sqlConnection.CreateCommand())
{
string updateSql = null;
if (inMoney)
{
updateSql = "update Account set Balance=Balance+@Money where Id=@Id;";
//updateSql = string.Format("update Account set Balance=Balance+{0} where Id={1}", money, id)+";"; }
else
{
updateSql = "update Account set Balance=Balance-@Money where Id=@Id;";
//updateSql = string.Format("update Account set Balance=Balance-{0} where Id={1}", money, id)+";"; } command.CommandText = updateSql;
command.CommandType = CommandType.Text;
command.Parameters.Add(new SqlParameter("@Id", id));
command.Parameters.Add(new SqlParameter("@Money", money)); command.ExecuteNonQuery();
}
}
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding_translationBind" transactionFlow="true"></binding>
</wsHttpBinding>
</bindings>
<services>
<service name="Keasy5.WCF.Transaction.WCFService.Service1">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Transaction.WCFService/Service1/" />
</baseAddresses>
</host>
<endpoint address="bank"
binding="wsHttpBinding"
bindingConfiguration="wsHttpBinding_translationBind"
contract="Keasy5.WCF.Transaction.WCFService.IService1"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel> </configuration>
System.InvalidOperationException: “Service1”协定上至少有一个操作配置为将 TransactionFlowAttribute 特性设置为“强制”,但是通道的绑定“BasicHttpBinding”未使用 TransactionFlowBindingElement 进行配置。没有 TransactionFlowBindingElement,无法使用设置为“强制”的 TransactionFlowAttribute 特性。
在 System.ServiceModel.Dispatcher.TransactionValidationBehavior.ValidateTransactionFlowRequired(String resource, String name, ServiceEndpoint endpoint)
在 System.ServiceModel.Dispatcher.TransactionValidationBehavior.System.ServiceModel.Description.IServiceBehavior.Validate(ServiceDescription service, ServiceHostBase serviceHostBase)
在 System.ServiceModel.Description.DispatcherBuilder.ValidateDescription(ServiceDescription description, ServiceHostBase serviceHost)
在 System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost)
在 System.ServiceModel.ServiceHostBase.InitializeRuntime()
在 System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
在 System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
在 Microsoft.Tools.SvcHost.ServiceHostHelper.OpenService(ServiceInfo info)
解决方法是:为endpoint 配置一个transactionFlow="true"的Binding。
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding_translationBind" transactionFlow="true"></binding>
</wsHttpBinding>
</bindings>
<endpoint address="bank"
。。。
bindingConfiguration="wsHttpBinding_translationBind"
。。。。
></endpoint>
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1"
transactionFlow="true" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Transaction.WCFService/Service1/bank"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService1"
contract="TransactionWCFService.IService1"
name="Transaction_IService1">
</endpoint>
</client>
</system.serviceModel>
</configuration>
private void buttonChangedAccount_Click(object sender, EventArgs e)
{
int outId = Convert.ToInt32(this.textBoxOutId.Text);
int inId = Convert.ToInt32(this.textBoxInId.Text);
double money = Convert.ToDouble(this.textBoxMoney.Text); Service1Client client = new Service1Client("Transaction_IService1"); using (System.Transactions.TransactionScope transactionScope = new TransactionScope())
{
client.OutMoney(outId, money);
client.IntoMoney(inId, money); transactionScope.Complete();
}
}
【3】测试事务
到目前,转账功能完成,
为测试事务是否成功,服务端或客户端故意中抛出一个异常。
private void buttonChangedAccount_Click(object sender, EventArgs e)
{
int outId = Convert.ToInt32(this.textBoxOutId.Text);
int inId = Convert.ToInt32(this.textBoxInId.Text);
double money = Convert.ToDouble(this.textBoxMoney.Text); Service1Client client = new Service1Client("Transaction_IService1"); using (System.Transactions.TransactionScope transactionScope = new TransactionScope())
{
try
{
client.OutMoney(outId, money);
client.IntoMoney(inId, money); throw new FaultException("发生故障,转账失败"); transactionScope.Complete();
}
catch (FaultException faultException)
{
System.Transactions.Transaction.Current.Rollback();
MessageBox.Show(faultException.Message);
} }
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single/*对象用于所有传入呼叫,并且在调用后不回收。如果服务对象不存在,则创建一个。*/
,ReleaseServiceInstanceOnTransactionComplete = false /*获取或设置一个值,该值指定在完成当前事务后是否释放服务对象*/
)]
public class Service1 : IService1
{
。。。。。
[OperationBehavior(TransactionScopeRequired = true /*该值指示方法在执行时是否需要事务范围*/)]
public void IntoMoney(int id, double money)
{
ChangeMoneyToDb(id, money, true); throw new FaultException("发生故障,转账失败");
} 。。。。。。。。。
出现异常后,两个账号的余额均没有改变。事务起效了。
如果调用不支持事务的服务方法,会是什么样子的
为了进行探究:在IService和Service1,添加两个不支持事务的两个方法:
OutMoneyNoTransaction和IntoMoneyNoTransaction
IService1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace Keasy5.WCF.Transaction.WCFService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory/*强制事务必须成为流*/)]
void OutMoney(int id, double money); [OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory/*强制事务必须成为流*/)]
void IntoMoney(int id, double money); [OperationContract]
void OutMoneyNoTransaction(int id, double money); [OperationContract]
void IntoMoneyNoTransaction(int id, double money);
}
}
Service1.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace Keasy5.WCF.Transaction.WCFService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single/*对象用于所有传入呼叫,并且在调用后不回收。如果服务对象不存在,则创建一个。*/
,ReleaseServiceInstanceOnTransactionComplete = false /*获取或设置一个值,该值指定在完成当前事务后是否释放服务对象*/
)]
public class Service1 : IService1
{
private const string ConnectString = "Data Source=.;Initial Catalog=WCFTransactionDb;Integrated Security=True"; 。。。。 /// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="money"></param>
/// <param name="inMoney">inMoney=true表示转入,否则表示转出</param>
private static void ChangeMoneyToDb(int id, double money, bool inMoney)
{
using (SqlConnection sqlConnection = new SqlConnection(ConnectString))
{
sqlConnection.Open(); using (SqlCommand command = sqlConnection.CreateCommand())
{
string updateSql = null;
if (inMoney)
{
updateSql = "update Account set Balance=Balance+@Money where Id=@Id;";
//updateSql = string.Format("update Account set Balance=Balance+{0} where Id={1}", money, id)+";"; }
else
{
updateSql = "update Account set Balance=Balance-@Money where Id=@Id;";
//updateSql = string.Format("update Account set Balance=Balance-{0} where Id={1}", money, id)+";"; } command.CommandText = updateSql;
command.CommandType = CommandType.Text;
command.Parameters.Add(new SqlParameter("@Id", id));
command.Parameters.Add(new SqlParameter("@Money", money)); command.ExecuteNonQuery();
}
}
} public void OutMoneyNoTransaction(int id, double money)
{
ChangeMoneyToDb(id, money, false);
} public void IntoMoneyNoTransaction(int id, double money)
{
ChangeMoneyToDb(id, money, true); throw new FaultException("发生故障,转账失败");
}
}
}
客户端和服务端的配置不需要改变。
客户端的调用,如下:
private void ChangeAccountNoTrasacion_Click(object sender, EventArgs e)
{
int outId = Convert.ToInt32(this.textBoxOutId.Text);
int inId = Convert.ToInt32(this.textBoxInId.Text);
double money = Convert.ToDouble(this.textBoxMoney.Text); Service1Client client = new Service1Client("Transaction_IService1"); using (System.Transactions.TransactionScope transactionScope = new TransactionScope())
{
try
{
client.OutMoneyNoTransaction(outId, money);
client.IntoMoneyNoTransaction(inId, money); transactionScope.Complete();
}
catch (FaultException faultException)
{
System.Transactions.Transaction.Current.Rollback();
MessageBox.Show(faultException.Message);
} }
}
虽然调用服务的两个不支持事务的方法被放在:
transactionScope = new TransactionScope()中
using (System.Transactions.TransactionScope transactionScope = new TransactionScope())
{
。。。。
client.OutMoneyNoTransaction(outId, money);
client.IntoMoneyNoTransaction(inId, money);
。。。。
但是两个账号的余额还是发生了改变。事务没有起效。
其他事务
【1】SQL中的事务处理
我们可以通过如下三个SQL语句实现事务的启动、提交与回滚:
using (DbTransaction transaction = connection.BeginTransaction())
{ }
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace WindowsFormsApplication1
{
class Class1
{
SqlConnection conn; //连接对象
SqlTransaction tran; //事务对象
public Class1()
{
conn = new SqlConnection("server=.;uid=sa;pwd=sa;database=master");
}
//转出
private void OutMoney(int m)
{
SqlCommand cmd = new SqlCommand("update account set balance=balance-" + m + " where ID='A'", conn);
cmd.Transaction = tran;
cmd.ExecuteNonQuery();
} //转入
private void intoMoney(int m)
{
SqlCommand cmd = new SqlCommand("update account set balance=balance+" + m + " where ID='B'", conn);
cmd.Transaction = tran;
cmd.ExecuteNonQuery();
} //公开转帐
public void TransferMoney(int m)
{
conn.Open();
tran = conn.BeginTransaction(); //开启事务
try
{
OutMoney(m);
intoMoney(m);
tran.Commit(); //提交事务
}
catch (Exception err)
{
tran.Rollback(); //回滚事务
}
finally
{
conn.Close();
}
}
}
}
源码下载
链接: http://pan.baidu.com/s/1pJPqg7h 密码: 6yjh
【WCF--初入江湖】07 分布式事务的更多相关文章
- [转载]WCF系列_分布式事务(下)
浏览到chnking的WCF的分布式事务处理不错,转载过来分享一下. 1. WCF分布式事务例子这里也用转账的例子说事.用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B ...
- WCF分布式事务(EF)
才说分布式事务,首先,了解一下什么是交易. 事务有四个特性:ACID A是Atomicity,原子性.一个事务往往涉及到很多的子操作,原子性则保证这些子操作要么都做,要么都不做,而不至于出现事务的部分 ...
- WCF(三)分布式事务
最近在学WCF,所以有两个设想疑问(菜鸟多疑问): 如果有WCF服务A,WCF服务B,客户端调用WCF服务A插入一条数据,然后再调用服务B也插入一条数据,然而服务B出错了进行了回滚,服务A能不能也进行 ...
- WCF分布式开发步步为赢(12):WCF事务机制(Transaction)和分布式事务编程
今天我们继续学习WCF分布式开发步步为赢系列的12节:WCF事务机制(Transaction)和分布式事务编程.众所周知,应用系统开发过程中,事务是一个重要的概念.它是保证数据与服务可靠性的重要机制. ...
- oracle分布式事务总结-转载
基本概念 Local Coordinator:在分布事务中,必须参考其它节点上的数据才能完成自己这部分操作的站点. Global Coordinator:分布事务的发起者,负责协调这个分布事务. Co ...
- 谈谈分布式事务之三: System.Transactions事务详解[上篇]
在.NET 1.x中,我们基本是通过ADO.NET实现对不同数据库访问的事务..NET 2.0为了带来了全新的事务编程模式,由于所有事务组件或者类型均定义在System.Transactions程序集 ...
- 谈谈分布式事务之二:基于DTC的分布式事务管理模型[下篇]
[续上篇] 当基于LTM或者KTM的事务提升到基于DTC的分布式事务后,DTC成为了本机所有事务型资源管理器的管理者:此外,当一个事务型操作超出了本机的范 围,出现了跨机器的调用后,本机的DTC需要于 ...
- WCF学习笔记之事务编程
WCF学习笔记之事务编程 一:WCF事务设置 事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元: WCF通过System.ServiceModel.TransactionFlowA ...
- .NET Core 事件总线,分布式事务解决方案:CAP 基于Kafka
背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...
随机推荐
- HDU1042 N! 大数的阶乘
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1042 由于数字特别大, 所以用数组来模拟. 经测试, 数组大小开50000 可过. 附AC代码, 欢迎 ...
- 转帖:使用TortoiseGit处理代码冲突
原址:http://www.cnblogs.com/jason-beijing/p/5718190.html 场景一 user0 有新提交 user1 没有pull -> 写新代码 -&g ...
- Qt获得网页源码
1.工程中添加网络模块 打开你的.pro文件插入以下代码 QT += network 2.添加代码 CodeQString NetWork::getWebSource(QUrl url) { QNet ...
- 《Apache数据传输加密、证书的制作》——涉及HTTPS协议
首先了解http和https: HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议. HTTPS和HTTP的区别: http是超文本传输协议,信息是明 ...
- WPF 绑定四(层级绑定)
xaml: <Window x:Class="WpfApplication1.Window4" xmlns="http://schemas.microsoft.co ...
- ASP.ENT Core Linux 下 为 donet创建守护进程(转载)
原文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-supervisor.html 前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 ...
- mysql怎么从1开始递增
前提:使用SQLyog数据库管理工具 1.打开更改表: 2.点击表字段下方“高级属性”: 3.找到“自动递增”这一项,值改为1: 4.点击“确定”关闭高级属性表弹出框: 5.点击“Alter”关闭更改 ...
- PHP 超级全局变量
超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用. PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们在一个脚本的全部作 ...
- sql语句中like匹配的用法详解
在SQL结构化查询语言中,LIKE语句有着至关重要的作用. LIKE语句的语法格式是:select * from 表名 where 字段名 like 对应值(子串),它主要是针对字符型字段的,它的作用 ...
- Microsoft Press Free eBook
微软的免费的电子书, 都是Microsoft Press 出版的 有以下价格方面 Windows Server(大体上都是Windows Server 2012 ) Microsoft Azure(好 ...