在WCF中实现事务

1. WCF服务中事务的边界

WCF服务中,事务是以方法为边界的,每个WCF服务的方法可以有独立事务的执行模式。而事务可以在多个服务中传播,也可以在服务端与客户端之间传播,介时事务管理器的级数将会晋升。

2. 简单的事务使用方式

TransactionScopeRequired与TransactionAutoComplete是WCF事务的基本元素。

  当TransactionScopeRequired等于true时,代表在此WCF服务的方法中启动事务。反之,当此值为false时代表此方法不执行事务。

  当TransactionAutoComplete等于true时,代表该方法使用隐式事务,这也是微软推荐使用的方法。即当该方法在运行过程中没有抛出Exception,操作就默认为完成,事务将自动提交。如果期间出现任何异常,事务就会自动回滚。如果TransactionAutoComplete等于false时,该方法即为显式事务,即需要在方法完成时利用OperationContext.Current.SetTransactionComplete () 显式提交事务。

[ServiceContract(SessionMode=SessionMode.Required)]
public interface IService
{
[OperationContract]
void Method1();

[OperationContract]
void Method2();
}

public class Service : IService
{
//隐式事务
[OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=true)]
public void Method1()
{
...........
}

//显式事务
[OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=false)]
public void Method2()
{
...........
OperationContext.Current.SetTransactionComplete();
}
}
3. 事务的传播

在同一个应用程序域中,事务默认能相互传播,在上面的例子当中,当方法Method1()直接调用Mehtod2()的时候,事务也能够成功流转。

  事务也能够在服务端与客户端之间传播,还能跨越服务边界,在多个系统当中流转,在WCF里把服务中的事务传播称作事务流(Transaction Flow)。如果事务流需要在服务端和客户端成功传播或使用分布式事务,必须具备以下条件:

  • 绑定必须支持事务,在WCF内并非所有绑定都支持事务,像常用的BasicHttpBinding就不支持事务的传播。只有以下几个绑定才能支持事务流的运转:NetTcpBinding、WSHttpBinding、WSDualHttpBinding、WSFederationHttpBinding、NetNamedPipeBinding。
  • 服务方法必须设置好TransactionScopeRequired与TransactionAutoComplete两个事务的基本元素,要成功启动事务,这是基础条件。
  • 把TransactionFlow设置为true,这代表启动事务流,允许在SOAP头部放置事务的信息。一般情况下TransactionFlow的默认值为false ,这表示事务只能在服务器的同一应用程序域内流转,而不能实现服务端与客户端之间的传播。
  • 把服务契约的TransactionFlowOption设置为Allowed,这代表允许客户端的事务传播到服务端。
  • 客户端必须启动一个事务,在最后使用TransactionScope.Complete ( ) 提交事务。

TransactionFlowOption说明

  TransactionFlowOption有三个选项:

  一为NotAllowed,这代表了禁止客户端传播事务流到服务端,即使客户端启动了事务,该事务也会被忽略;

  二为Allowed,这代表允许客户端的事务传播到服务端,但服务器端不一定会引用到此事务;

  三为Mandatory,这代表服务端与客户端必须同时启动事务流,否则就会抛出InvalidOperationException异常。

  下面举几个例子来讲解一下事务流的使用方式。

 3.1 在服务端与客户端之间传播事务

 这是事务流的基本使用方式,首先在服务端使用wsHttpBinding绑定建立一个服务契约,在方法中利用TransactionInformation对象检测一下事务的状态。然后设置好TransactionScopeRequired与TransactionAutoComplete属性来启动事务,在*.config中把TransactionFlow设置为true。再把服务的TransactionFlowOption设置为Allowed,最后在客户端通过TransactionScope.Complete()的方法提交事务。

  服务端:

namespace Example
{
[ServiceContract]
public interface IExampleService
{
[OperationContract]
void Method1();
}

public class ExampleService : IExampleService
{
//使用隐式事务,并把TransactionFlowOption设置为Allowed打开事务流
[OperationBehavior(TransactionAutoComplete=true,TransactionScopeRequired=true)]
[TransactionFlow(TransactionFlowOption.Allowed)]
public void Method1()
{
//通过TransactionInformation检测事务状态
Transaction transaction = Transaction.Current;
string info=string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
transaction.TransactionInformation.DistributedIdentifier,
transaction.TransactionInformation.LocalIdentifier);
Console.WriteLine("Method1: \n"+info);
}

static void Main(string[] args)
{
//启动服务
ServiceHost exampleHost = new ServiceHost(typeof(Example.ExampleService));

exampleHost.Open();

Console.WriteLine("service start");
Console.ReadKey();
Console.WriteLine("service end");

exampleHost.Close();
}
}
}
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<!--启动事务流-->
<binding name="defaultWSHttpBinding" transactionFlow="true" />
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Example.ExampleService">
<!--使用支持事务流的wsHttpBinding绑定-->
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="defaultWSHttpBinding"
contract
="Example.IExampleService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:7200/Example/ExampleService/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>

  客户端:

class Program
{
static void Main(string[] args)
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
ShowTransactionMessage("start");

ExampleServiceReference.ExampleServiceClient exampleService1 = new
ExampleServiceReference.ExampleServiceClient();
exampleService1.Method1();
ShowTransactionMessage("exampleService started");
exampleService1.Close();
//事务提交
scope.Complete();
}
Console.ReadKey();
}

//检查事务的状态
static void ShowTransactionMessage(string data)
{
if (Transaction.Current != null)
{
Transaction transaction = Transaction.Current;
string info = string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
transaction.TransactionInformation.DistributedIdentifier,
transaction.TransactionInformation.LocalIdentifier);
Console.WriteLine(data+" \n" + info);
}
}
}
  3. 2使用分布式事务协调多个服务端的操作

在分布式系统当中,单个客户端可能引用多个服务,分布式事务能协调多方的操作。多个系统中的操作要不同时成功,要不同时失败。下面的例子中,客户端同时引用了ExampleService服务和ExtensionService服务,并启动了分布式事务。而在客户端与两个服务端的事务都是通过DistributedIndentifier 作为事务的标识的。

  服务端:

//*******************************ExampleService**********************************//
namespace Example
{
[ServiceContract]
public interface IExampleService
{
[OperationContract]
void Method1();
}

public class ExampleService : IExampleService
{
//使用隐式事务,并把TransactionFlowOption设置为Allowed
[OperationBehavior(TransactionAutoComplete=true,TransactionScopeRequired=true)]
[TransactionFlow(TransactionFlowOption.Allowed)]
public void Method1()
{
//通过TransactionInformation检测事务状态
Transaction transaction = Transaction.Current;
string info=string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
transaction.TransactionInformation.DistributedIdentifier,
transaction.TransactionInformation.LocalIdentifier);
Console.WriteLine("Method1: \n"+info);
}

static void Main(string[] args)
{
//启动服务
ServiceHost exampleHost = new ServiceHost(typeof(Example.ExampleService));

exampleHost.Open();

Console.WriteLine("service start");
Console.ReadKey();
Console.WriteLine("service end");

exampleHost.Close();
}
}
}
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<!--启动事务流-->
<binding name="defaultWSHttpBinding" transactionFlow="true" />
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Example.ExampleService">
<!--使用支持事务流的wsHttpBinding绑定-->
<endpoint address="" binding="wsHttpBinding" contract="Example.IExampleService"
bindingConfiguration
="defaultWSHttpBinding">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:7200/Example/ExampleService/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
//*************************************Extension********************************//
namespace Extension
{
[ServiceContract]
public interface IExtensionService
{
[OperationContract]
void DoWork();
}

public class ExtensionService : IExtensionService
{
[OperationBehavior(TransactionAutoComplete=true,TransactionScopeRequired=true)]
[TransactionFlow(TransactionFlowOption.Allowed)]
public void DoWork()
{
Transaction transaction = Transaction.Current;
string info = string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
transaction.TransactionInformation.DistributedIdentifier,
transaction.TransactionInformation.LocalIdentifier);
Console.WriteLine("DoWork: \n" + info);
}

static void Main(string[] args)
{
Console.WriteLine("extension service start");
ServiceHost host = new ServiceHost(typeof(ExtensionService));
host.Open();
Console.ReadKey();
host.Close();
}
}
}
<configuration>
<!---->
...................
</configuration>

  客户端

namespace Test
{
class Program
{
static void Main(string[] args)
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
ShowTransactionMessage("start");

ExampleServiceReference.ExampleServiceClient exampleService1 = new
ExampleServiceReference.ExampleServiceClient();
exampleService1.Method1();
ShowTransactionMessage("exampleService started");
ExtensionServiceReference.ExtensionServiceClient extensionService = new
ExtensionServiceReference.ExtensionServiceClient();
extensionService.DoWork();
ShowTransactionMessage("extensionService started");

exampleService1.Close();
extensionService.Close();

scope.Complete();
}

Console.ReadKey();
}

//检查事务的状态
static void ShowTransactionMessage(string data)
{
if (Transaction.Current != null)
{
Transaction transaction = Transaction.Current;
string info = string.Format(" DistributedIndentifier:{0} \n LocalIndentifier:{1}",
transaction.TransactionInformation.DistributedIdentifier,
transaction.TransactionInformation.LocalIdentifier);
Console.WriteLine(data+" \n" + info);
}

}
}
}

4. 事务的的隔离性

  事务的隔离性是通过TransactionIsolationLevel来定义的,它存在以下几个级别:

Unspecified  
ReadUncommitted 在读取数据时保持共享锁以避免读取已修改的数据,但在事务结束前这些数据可能已更改,因此会导致不可重复的读取和虚假数据。
ReadCommitted 发出共享锁定并允许非独占方式的锁定。
RepeatableRead 在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。这防止了不可重复的读取,但仍有可能产生虚假行。
Serializable 默认级别,也是最高级别。表示事务完成前禁止外界更新数据
Chaos 不使用隔离
Snapshot  

  需要注意服务端与客户端必须使用同一级别的隔离模式,否则系统将会抛出FaultException异常。

  服务类必须在至少一个方法开启了事务后才可以设置隔离模式

public interface IService
{
[OperationContract]
void Method();
}

[ServiceBehavior(TransasctionIsolationLevel=IsolationLevel.ReadCommitted)]
public class Service : IService
{
//隐式事务
[OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=true)]
public void Method()
{..........}
}

5. 事务的超时

  当事务实现隔离时,资源将会被锁定,如果一些事务长期占有资源,那将容易造成死锁,为了避免这个问题,事务有一个超时限制,这个超时默认值为60s。如果事务超过此时间,即使没有发生异常,也会自动中止。

  超时时候可以通过特性设置,也可使用*.config文件设置。下面的两段代码有着相同的效果,就是把超时时间设置为10s。

[ServiceBehavior(TransactionTimeout="00:00:10")]
public class Service:IService
{......}
 <configuration>
........
<system.serviceModel>
........
<services>
<service name="MyService" behaviorConfiguration="myBehavior">
......
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="myBehavior" transactionTimeout="00:00:10"/>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值