在上一篇详细讲解wcf编程分布式事务应用理论篇 ,我们讲解了一些wcf分布式事务的理论知识,那么接着我们就来一下wcf分布式事务例子吧。这样更加入解wcf的应用。
1、 WCF分布式事务例子
这里也用转账的例子说事。
用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B的相应账户的资金就增加多少。
系统A机器上有数据库AccountA,系统B机器上有数据库AccountB,数据库的结构一样,都有一个数据表Account,结构如下:
|
字段
|
数据类型
|
含义
|
|
depositorID
|
int
|
账户id
|
|
amount
|
decimal(18, 2)
|
金额
|
为了演示TxF事务性文件,在系统B中增加了一个写文件的操作,记录本次转账操作的信息。转账的所有操作步骤:系统A上账户上减少金额,系统B上记录转账信息文件,系统B上相应账户资金增加这三个操作都在一个事务流中,要么全部完成,要么全部回滚。
系统A和系统B分别在服务器A和服务器B上。系统A在账户上减少金额后调用系统B的WCF服务,在系统B中继续增加账户资金,生成转账信息文件。
下面开始这个例子的完整过程。
1.1. 建立系统B转账WCF服务
建立服务契约:

Code
[http://www.xueit.com]
[ServiceContract]
public interface IAccountB
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void deposit(int depositorid, double amount);
}
服务契约就一个方法deposit,其中depositorid表示账户id,amount表示要从系统A转账到系统B的金额。
TransactionFlow这个属性指示operation是否跟随调用端的事务流,参数含义:
TransactionFlowOption.NotAllowed – 表示此operation不跟随传入的事务流,不参与分布式事务。
TransactionFlowOption.Allowed – 表示此opreation可以跟随传入的事务流,如果有传入的事务流则参与,如果没有传入的事务流则不参与,但是可以启动本地的事务。
TransactionFlowOption.Mandatory – 表示此operation必须跟随传入的事务,参与分布式事务,如果调用此operation的客户端没有事务流则抛出异常。
下面是服务实现:

Code
[http://www.xueit.com]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class AccountBService : IAccountB
{
[OperationBehavior(TransactionScopeRequired = true)]
public void deposit(int depositorid, double amount)
{
#region 新建事务性文件
string path = @"c:\test.txt";
FileStream fs = TransactedFile.Open(path, System.IO.FileMode.Create,
System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);
string fileContent = string.Format("从系统A转账到系统B\r\n用户ID:{0}\r\n转账金额为:{1}", depositorid.ToString(), amount.ToString());
byte[] byteArrar = Encoding.UTF8.GetBytes(fileContent);
fs.Write(byteArrar, 0, byteArrar.Count());
fs.Flush();
fs.Close();
#endregion
#region 数据访问,在指定账户上增加存款
string connstr = ConfigurationManager.ConnectionStrings["ConnStr"].ToString();
SqlCommand mySqlCommand = new SqlCommand("update account set amount = amount @amount where depositorid = @depositorid ");
mySqlCommand.Connection = new SqlConnection(connstr);
SqlParameter par1 = new SqlParameter("@amount", SqlDbType.Decimal);
par1.Value = amount;
mySqlCommand.Parameters.Add(par1);
par1 = new SqlParameter("@depositorid", SqlDbType.Int);
par1.Value = depositorid;
mySqlCommand.Parameters.Add(par1);
mySqlCommand.Connection.Open();
mySqlCommand.ExecuteNonQuery();
mySqlCommand.Connection.Close();
#endregion
}
}
服务实现了deposit操作。
[OperationBehavior(TransactionScopeRequired = true)],这里的TransactionScopeRequired = true表示这个操作在TransactionScope内执行,加上前面OperationContract上的TransactionFlowOption.Allowed 允许跟随事务的设置,这个deposit的操作将会参与客户端发起的分布式事务。
实现的deposit操作中完成两个任务,先转账信息写入c:\test.txt文件,这里写文件操作使用了TxF事务性文件操作TransactedFile.Open,关于TxF的操作部分的代码微软有提供,在本文中提供的代码中包含了这部分源码。使用事务性文件操作,在事务中的其他事务资源操作失败后,文件操作也会回滚。
1.2. 建立系统A转账客户端
系统A是个Console应用:

Code
[http://www.xueit.com]
static void Main(string[] args)
{
ChannelFactory<IAccountB> myFactory = new ChannelFactory<IAccountB>("endpointConfig");
IAccountB myClient = myFactory.CreateChannel();
Double amount = 500;
int depositorid = 1;
using (TransactionScope scop = new TransactionScope())
{
#region 数据访问,在指定账户上减少存款
string connstr = ConfigurationManager.ConnectionStrings["ConnStr"].ToString();
SqlCommand mySqlCommand = new SqlCommand("update account set amount = amount - @amount where depositorid = @depositorid ");
mySqlCommand.Connection = new SqlConnection(connstr);
SqlParameter par1 = new SqlParameter("@amount", SqlDbType.Decimal);
par1.Value = amount;
mySqlCommand.Parameters.Add(par1);
par1 = new SqlParameter("@depositorid", SqlDbType.Int);
par1.Value = depositorid;
mySqlCommand.Parameters.Add(par1);
mySqlCommand.Connection.Open();
mySqlCommand.ExecuteNonQuery();
mySqlCommand.Connection.Close();
#endregion
try
{
myClient.deposit(depositorid, amount);
scop.Complete();
}
catch (Exception e)
{
Transaction.Current.Rollback();
}
}
}