WCF 事务 - 简要介绍






4.64/5 (18投票s)
关于 WCF 事务的初步信息
引言
我相信事务对你来说不是一个新概念,但你想了解 WCF 如何实现它。这篇简短的文章触及了 WCF 事务的一些边缘概念。它适用于对 WCF 有一定了解并想开始了解该概念的人。它提供了一些关于事物工作原理的基本想法,并为进一步阅读奠定了基础。事务是一个庞大的概念,无法在一篇文章中解释清楚。
Using the Code
在继续阅读之前,最好下载附件中的代码并在您的计算机上进行配置。文章会修改代码并解释其效果,因此边阅读边实践会更具说明性。
解释
什么是事务?
虽然我们理解,但还是重申一下——事务意味着一组符合 ACID 原则的操作。
- 原子性 (Atomic) - 要么所有操作都对系统产生影响,要么都不产生。
- 一致性 (Consistency) – 无论操作集如何,系统始终处于一致状态。
- 隔离性 (Isolation) – 系统的中间状态不会暴露给外部世界。
- 持久性 (Durability) – 系统的状态将被持久化,不会易失。
一般来说,在 WCF 中通常会遇到两种事务——一种是客户端发起某个事务,执行一些工作,然后将其传播到服务器以在其中完成一些工作;另一种是客户端不发起任何事务,但要求服务器在某个事务中执行某个操作。
当事务不跨越其 Appdomain
的边界时,它是本地事务,否则可以视为分布式事务。WCF 提供了强大的支持来处理这些事务,我们将在稍后介绍。
事务是一个很大的话题,无法在这篇短文中涵盖,但它将提供一个关于如何开始使用它的简要介绍。
如何在 WCF 中配置事务?
当我们分析某些逻辑足够关键,值得用事务包装(例如账户的贷记-借记)时,首先应确定逻辑的哪个部分——服务端、客户端还是两者都需要事务。事务可能只需要在服务内部,或者可能在客户端发起然后传播到服务,或者可能仅在客户端。这个决定在实际配置事务时会很有帮助。
在 WCF 中配置事务时需要考虑一些因素:
绑定 (Binding) - WCF 中只有少数绑定支持事务,它们是 NetTcpBinding
、NetNamedPipeBinding
、WSHttpBinding
、WSDualHttpBinding
和 WSFederationHttpBinding
。因此,为了配置事务,必须从这些绑定中选择一个。尽管这些绑定支持事务,但默认情况下它是禁用的,因此需要手动启用它们。
操作契约和行为 (Operation Contract and behavior) - 合适的绑定仅仅为事务从客户端到服务的传播提供了途径,但在服务端,只有一个操作/方法将负责处理事务。因此,下一步是配置操作。操作通常使用以下两个属性进行配置:
A) TransactionFlow
– 此属性设置为一个名为 TransactionFlowOption
的参数,该参数有三个值。这些值不言自明:
- Allowed – 表示如果客户端有事务,则接受;否则不构成问题。
- NotAllowed – 客户端在调用方法时不能向服务器发送事务。
- Mandatory – 客户端必须创建一个事务,否则无法调用该方法。
B) TransactionScopeRequired
- 此 OperationBehavior
描述了方法/操作是否应在事务中执行其工作。
现在我们将它们放在一起。有一些逻辑应该放在某个事务中。WCF 有一些支持事务的绑定,但默认情况下是禁用的。要启用事务从客户端到服务的流,应启用绑定支持。在绑定支持就绪后,通过设置 TransactionFlow
属性来配置方法以接受来自客户端的事务。最后,配置 TransactionScopeRequired
,以便服务方法可以在事务中运行。
让我们进行不同的配置,看看会发生什么。
在深入实验之前,我先告诉你,在附件的示例中,我们有一个简单的服务,其配置最少,使其能够正常工作,一个带有按钮点击事件的客户端 WinForm 应用程序,以及一个简单的数据库 emp
表。现在我们将进行一些实验,看看当我们执行某些操作时会发生什么。
- 第一次尝试时,我们将只用
TransactionScopeRequired = false
来修饰upload
方法。代码将如下所示:
[OperationBehavior (TransactionScopeRequired=false)]
public void upload()
{
// some code...
}
现在运行应用程序(按 Ctrl + F5 以发布模式运行),尽管您会看到一些错误,但 Emp
表中仍会获得一些数据。Emp
表获得数据是因为该方法不在某个事务中。这次,只需将此属性设置为 true
,看看会发生什么。您的代码将如下所示:
[OperationBehavior(TransactionScopeRequired=true)]
public void upload()
{
// Some code...
}
这次,数据库不会获得新条目。这是因为该方法在某个事务下运行,而该事务仅在代码执行没有错误时才提交。这里,我们只是用“TransactionScopeRequired
”属性修饰了该方法,WCF 会处理其余的事情。如果 WCF 在执行过程中未发现任何异常,它会提交事务,而不会在意其他任何事情。我们可以通过另一个属性“TransactionAutoComplete
”来控制此默认行为。默认情况下,其值为 true
,但如果指定为 false
,即使代码中没有异常,WCF 也不会提交事务,然后您需要自己提交。
只需让您的代码看起来像这样:
[OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=false )] public void upload() { // some code… }
您必须按照如下所示使用以下属性来修饰您的服务合同。这是因为 TransactionAutoComplete=false
仅适用于有会话的调用。
[ServiceContract(SessionMode = SessionMode.Required)]
public interface Iservice
{
//some code…
}
只需注释掉在服务方法中引发异常的行并运行代码。尽管您不会收到任何异常,但数据库仍未获得新条目。这是因为 WCF 没有按默认方式工作,而是由您来提交事务。现在,在您刚刚注释掉的行之后,写入以下内容以显式提交事务:
OperationContext.Current.SetTransactionComplete();
运行代码,这次您的数据库将获得新条目。这是因为您正在显式地提交事务。
我们来总结一下。到目前为止,我们已经通过使用名为 TransactionScopeRequired
和 TransactionAutoComplete
的两个属性来处理方法。TransactionScopeRequired
指定方法是否将在某个事务中执行,而另一个属性 TransactionAutoComplete
指定 WCF 是否可以在没有错误的情况下提交事务,或者是否需要程序员来提交事务。
在下一节中,我们将从客户端传播事务到服务。
之前,我们观察到,如果配置了“TransactionScoperequired
”,服务中的方法将在某个事务下运行,并且该事务是在服务方法本身中创建的。到目前为止一切顺利,但如果客户端有一些代码与服务协同工作,并期望作为一个整体提交(客户端逻辑 + 服务器逻辑),该怎么办?在这种情况下,客户端将需要一个事务,客户端将在该事务下完成其工作,然后将其传递给服务,服务将在其中完成其工作,并且只有在一切正常的情况下,才会由客户端提交。为了使事务从客户端流向服务,需要进行一些配置。
- 绑定配置 – 因为 WCF 中只有少数绑定支持事务流。
- 操作行为 - 我们需要告诉操作/方法如何响应事务。
让我们看看如何使用它。再次解压新的代码,或者撤销所有已做的更改,然后打开服务的配置文件,并在 endpoint 中添加 bindingConfiguration="newbinding"
,并相应地指定绑定配置。
<bindings>
<wsHttpBinding>
<binding name="newbinding" transactionFlow ="True" />
</wsHttpBinding>
</bindings>
由于我们已经有了支持事务的绑定,因此无需更改绑定。通过 TransactionFlow
属性启用操作契约以接受客户端的事务,如下所示:
[ServiceContract]
public interface Iservice
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void upload();
}
确保服务方法已用 TransactionScopeRequired
修饰,如下所示:
[OperationBehavior(TransactionScopeRequired = true)] public void upload() { // some code... }
生成新的代理,并使用较新的服务类和 app.config 更新客户端。
在客户端,添加对 System.Transaction
的引用,并将按钮点击事件处理程序修改为如下所示:
try
{
using(TransactionScope scope = new TransactionScope())
{
IserviceClient client = new IserviceClient();
client.upload();
client.upload();
scope.Complete();
MessageBox.Show("All is well!!!");
}
}
catch
{
MessageBox.Show("Some error occurred...");
}
请注意代码,我们已将逻辑用 Transaction scope 包围起来。此事务范围会流向服务,然后在服务中,代码在该事务下运行。这里的 scope.complete()
负责提交事务。如果删除此行,事务将不会被提交,数据库也不会保留任何更改。
如果您交换上面代码中的第二个 client.upload()
和 scope.complete()
,那么第二个服务调用将无效。同样,您可以尝试 TransactionFlow
、TransactionScopeRequired
、TransactionAutoComplete
等属性的不同组合,以学习更多内容。
暂时就这些。让我们快速回顾一下。
- 在 WCF 中,只有少数绑定支持事务。
- 默认情况下,这些绑定的事务流是禁用的。
TransactionFlow
和TransactionScopeRequired
属性是配置服务操作/方法所必需的。- WCF 的自动提交行为可以通过
TransactionAutoComplete
属性进行控制。
希望这篇小文章能让您对 WCF 事务有所了解,现在您将能够自行进行实验。我建议您查阅 MSDN 以深入分析该概念。
尽管我已尽力确保内容无误,但如果有什么遗漏或需要更正的地方,请告诉我,这将对我们所有人都有帮助。
祝您进一步阅读一切顺利。
编程愉快。