WCF在预定义绑定中实现了标准的WSAtomicTranscation(WS-AT)协议和Microsoft专有的OleTx协议,这些协议可以用来在消息中加入事务状态的信息。我们可以指定将一个操作的代码放在事务范围里执行。
我们需要在Binding的配置节里指定,transcationFlow=true:
<bindings>
<wsHttpBinding>
<binding name="TranscationBindingConfig" transactionFlow="true">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
注意:任何被指定的必须在一个事务范围里执行的操作(OperationContract)都不能标记为单向方法(IsOneWay=true),因为在操作结束时,有关事务状态的信息必须传回给调用者。开发者也可以指出,当没有异常发生时,WCF应该自动为操作提交事务。
namespace WcfTranServiceLib
{
[ServiceContract]
public interface IDAOService
{
[OperationContract(IsOneWay=false)]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void InsAndUpdProduct(Products product);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void InsAndUpdSupplier(Suppliers supplier);
[OperationContract]
List<Products> GetProducts(string productID);
[OperationContract]
List<Suppliers> GetSuppliers(string supplierID);
}
}
如果需要在服务实现的操作需要自己来提交事务,可以使用WCF的静态System.ServiceModel.OperationContext对象:
public class MyServiceType
{
[OperationBehavior(TranscationScopeRequire=true,
TrancationAutoComplete=false)]
void MyMethod()
{
// 其他的业务逻辑
OperationContext.Current.SetTranscationComplete();
}
}
而客户端开发者可以使用System.Transcation命名空间提供的语法将服务的操作放进一个事务范围内。
using(TranscationScope ts = new TranscationScope(TranscationScopeOption.RequireNew))
{
client.DoSomething();
...
ts.Complete();
}
client.Close();
如果服务的操作支持事务,而且也配置了支持传送有关事务状态的绑定,那么客户端的事务直到服务操作决定提交时才会提交(TranscationScope的Complete被调用)。相反地,服务在客户端事务范围里对事务资源所做的任何操作都将被回滚。(客户端处理的过程中出现任何异常)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
namespace WcfTranClient
{
class Program
{
static void Main(string[] args)
{
try
{
var client = new DaoSvc.DAOServiceClient();
using (var ts = new TransactionScope())
{
DaoSvc.Suppliers s = new DaoSvc.Suppliers();
s.SupplierID = "S02";
s.CompanyName = "CompanyName1";
s.Address = "Address";
s.Phone = "123456";
client.InsAndUpdSupplier(s);
DaoSvc.Products p = new DaoSvc.Products();
p.ProductID = "P02";
p.ProductName = "ProductName1";
p.UnitPrice = 5;
p.SupplierID = "S02";
client.InsAndUpdProduct(p);
//throw new Exception("Test Exception");
ts.Complete();
}
var ps = client.GetProducts(null);
Console.WriteLine("Count of products: {0}", ps.Count);
var ss = client.GetSuppliers(null);
Console.WriteLine("Count of suppliers: {0}", ss.Count);
client.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
Console.Read();
}
}
}
}