在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>

### 下载 Popper.min.js 文件的方法 对于希望获取 `popper.min.js` 的开发者来说,可以通过多种方式来实现这一目标。通常情况下,推荐通过官方渠道或可靠的分发网络 (CDN) 来获得最新的稳定版文件。 #### 使用 CDN 获取 Popper.min.js 最简单的方式之一是从流行的 CDN 中加载所需的 JavaScript 库。这不仅简化了集成过程,还可能提高性能,因为许多用户已经缓存了来自这些服务提供商的内容。例如: ```html <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2/dist/umd/popper.min.js"></script> ``` 这种方式不需要手动下载文件到本地服务器;只需将上述 `<script>` 标签添加至 HTML 文档中的适当位置即可立即使用 Popper 功能[^1]。 #### 从 npm 或 yarn 安装 如果项目采用模块化构建工具链,则可以直接利用包管理器如 npm 或 Yarn 进行安装。命令如下所示: ```bash npm install @popperjs/core # 或者 yarn add @popperjs/core ``` 之后可以根据具体需求引入特定功能模块,而不是整个库,从而减少打包后的体积并优化加载速度[^2]。 #### 访问 GitHub 发布页面下载压缩包 另一种方法是访问 Popper.js 的 [GitHub Releases](https://github.com/popperjs/popper-core/releases) 页面,在这里可以选择不同版本的 tarball 或 zip 归档进行下载解压操作。这种方法适合那些偏好离线工作环境或是想要定制编译选项的人群[^3]。 #### 手动克隆仓库 最后一种较为少见但也可行的办法便是直接克隆完整的 Git 存储库副本。这样可以获得开发分支以及历史记录等更多信息,适用于贡献代码或者深入学习内部机制的情况。 ```bash git clone https://github.com/popperjs/popper-core.git cd popper-core ``` 完成以上任一途径后便能成功取得所需版本的 Popper.min.js 文件,并将其应用于个人项目之中[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值