介绍
WCF(Windows Communication Foundation) -
事务(Transaction):
·对契约方法
使用
TransactionFlowAttribute声明(设置TransactionFlowOption参
数
),以指定服务操作的事务流策略
·对
服务方法是用OperationBehaviorAttribute
声明(设置TransactionScopeRequired
参数),以指定方法是否在事务范围(TransactionScope
)内执行
·
配置host和client的binding节点的transactionFlow
属
性,以指定绑定是否支持流事务
示例
1、服务
Hello.cs
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.ServiceModel;
namespace
WCF.ServiceLib.Transaction
{
/**/
///
<summary>
///
IHello接口
///
</summary>
[ServiceContract]
public
interface
IHello
{
/**/
///
<summary>
///
打招呼方法
///
</summary>
///
<param name="name">
人名
</param>
///
<remarks>
///
TransactionFlow - 指定服务操作是否愿意接受来自客户端的传入事
务
///
NotAllowed - 禁止事务。默认值
///
Allowed - 允许事务
///
Mandatory - 强制事务
///
</remarks>
///
<returns></returns>
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void
WriteHello(
string
name);
}
/**/
///
<summary>
///
Hello类
///
</summary>
public
class
Hello : IHello
{
/**/
///
<summary>
///
打招呼方法
///
</summary>
///
<param name="name">
人名
</param>
///
<remarks>
///
OperationBehavior - 指定服务方法的本地执行行为
///
1、TransactionScopeRequired - 如果方法需要事务范围
才能执行,则为 true;否则为 false。默认值为 false
///
将 TransactionScopeRequired 设置为 true,可以要
求操作在事务范围内执行。如果流事务可用,则操作会在该事务内执行。如果流事务不可用,则会创建一个新事务并使用它来执行操作
///
2、TransactionAutoComplete - 默认值为 true
///
true - 当方法完成执行时,将把该事务标志为完成(自动提交事务)
///
false - 需要调用
OperationContext.Current.SetTransactionComplete()方法来手工配置该事务的正确完成;否则,该事务将
被标志为失败(手动提交事务)
///
</remarks>
///
<returns></returns>
[OperationBehavior(TransactionScopeRequired
=
true
, TransactionAutoComplete
=
true
)]
public
void
WriteHello(
string
name)
{
DBDataContext ctx
=
new
DBDataContext();
ctx.Items.InsertOnSubmit(
new
Item
{
Title
=
string
.Format(
"
Hello: {0}, TransactionId: {1}
"
, name, System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier),
CreatedTime
=
DateTime.Now
}
);
ctx.SubmitChanges();
}
}
}
Hi.cs
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.ServiceModel;
namespace
WCF.ServiceLib.Transaction
{
/**/
///
<summary>
///
IHi接口
///
</summary>
[ServiceContract]
public
interface
IHi
{
/**/
///
<summary>
///
打招呼方法
///
</summary>
///
<param name="name">
人名
</param>
///
<returns></returns>
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void
WriteHi(
string
name);
}
/**/
///
<summary>
///
Hi类
///
</summary>
public
class
Hi : IHi
{
/**/
///
<summary>
///
打招呼方法
///
</summary>
///
<param name="name">
人名
</param>
///
<returns></returns>
[OperationBehavior(TransactionScopeRequired
=
true
, TransactionAutoComplete
=
true
)]
public
void
WriteHi(
string
name)
{
if
(DateTime.Now.Second
%
2
==
0
)
throw
new
System.Exception(
"
为测试事务而抛出的异常
"
);
DBDataContext ctx
=
new
DBDataContext();
ctx.Items.InsertOnSubmit(
new
Item
{
Title
=
string
.Format(
"
Hi: {0}, TransactionId: {1}
"
, name, System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier),
CreatedTime
=
DateTime.Now
}
);
ctx.SubmitChanges();
}
}
}
Result.cs
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.ServiceModel;
namespace
WCF.ServiceLib.Transaction
{
/**/
///
<summary>
///
结果接口
///
</summary>
[ServiceContract]
public
interface
IResult
{
[OperationContract]
List
<
Item
>
GetResult();
}
/**/
///
<summary>
///
结果类
///
</summary>
public
class
Result : IResult
{
/**/
///
<summary>
///
返回数据库结果
///
</summary>
///
<returns></returns>
public
List
<
Item
>
GetResult()
{
DBDataContext ctx
=
new
DBDataContext();
var result
=
from l
in
ctx.Items
orderby l.CreatedTime descending
select l;
return
result.ToList();
}
}
}
2、宿主
Hello.svc
<%
@ ServiceHost Language
=
"
C#
"
Debug
=
"
true
"
Service
=
"
WCF.ServiceLib.Transaction.Hello
"
%>
Hi.svc
<%
@ ServiceHost Language
=
"
C#
"
Debug
=
"
true
"
Service
=
"
WCF.ServiceLib.Transaction.Hi
"
%>
Result.svc
<%
@ ServiceHost Language
=
"
C#
"
Debug
=
"
true
"
Service
=
"
WCF.ServiceLib.Transaction.Result
"
%>
Web.config
<?
xml version="1.0"
?>
<
configuration
>
<
system.serviceModel
>
<
behaviors
>
<
serviceBehaviors
>
<
behavior
name
="TransactionBehavior"
>
<!--
httpGetEnabled - 指
示是否发布服务元数据以便使用 HTTP/GET 请求进行检索,如果发布 WSDL,则为 true,否则为 false,默认值为 false
-->
<
serviceMetadata
httpGetEnabled
="true"
/>
<
serviceDebug
includeExceptionDetailInFaults
="true"
/>
</
behavior
>
</
serviceBehaviors
>
</
behaviors
>
<
services
>
<!--
name - 提供服务的类名
-->
<!--
behaviorConfiguration - 指定相关的行为配置
-->
<
service
name
="WCF.ServiceLib.Transaction.Hello"
behaviorConfiguration
="TransactionBehavior"
>
<!--
address - 服务地址
-->
<!--
binding - 通信方式
-->
<!--
contract - 服务契约
-->
<!--
bindingConfiguration - 指定相关的绑定配置
-->
<
endpoint
address
=""
binding
="wsHttpBinding"
contract
="WCF.ServiceLib.Transaction.IHello"
bindingConfiguration
="TransactionConfiguration"
/>
</
service
>
<
service
name
="WCF.ServiceLib.Transaction.Hi"
behaviorConfiguration
="TransactionBehavior"
>
<
endpoint
address
=""
binding
="wsHttpBinding"
contract
="WCF.ServiceLib.Transaction.IHi"
bindingConfiguration
="TransactionConfiguration"
/>
</
service
>
<
service
name
="WCF.ServiceLib.Transaction.Result"
behaviorConfiguration
="TransactionBehavior"
>
<
endpoint
address
=""
binding
="basicHttpBinding"
contract
="WCF.ServiceLib.Transaction.IResult"
/>
</
service
>
</
services
>
<
bindings
>
<
wsHttpBinding
>
<!--
transactionFlow - 指定该绑定是否应支持流事务
-->
<
binding
name
="TransactionConfiguration"
transactionFlow
="true"
/>
</
wsHttpBinding
>
</
bindings
>
</
system.serviceModel
>
</
configuration
>
3、客户端
Sample.aspx
<%
@ Page Language
=
"
C#
"
MasterPageFile
=
"
~/Site.master
"
AutoEventWireup
=
"
true
"
CodeFile
=
"
Sample.aspx.cs
"
Inherits
=
"
Transaction_Sample
"
Title
=
"
事务
(Transaction)
"
%>
<
asp:Content
ID
="Content1"
ContentPlaceHolderID
="head"
runat
="Server"
>
</
asp:Content
>
<
asp:Content
ID
="Content2"
ContentPlaceHolderID
="ContentPlaceHolder1"
runat
="Server"
>
<
p
>
<
asp:Label
ID
="lblErr"
runat
="server"
ForeColor
="Red"
/>
</
p
>
<
p
>
<
asp:Button
ID
="btnSubmit"
runat
="server"
Text
="事务测试"
OnClick
="btnSubmit_Click"
/>
<
br
/>
<
br
/>
<
asp:GridView
ID
="GridView1"
runat
="server"
>
</
asp:GridView
>
</
p
>
<
p
>
2PC(Two Phase Commitment Protocol)两阶段提交协议(WCF的事务的实现基
于此协议)
<
br
/>
实现分布式事务的关键就是两阶段提交协议。在此协议中,一个或多个资源管理器的活动均由一个称为事务协调器的单
独软件组件来控制。此协议中的五个步骤如下:
<
br
/>
1、应用程序调用事务协调器中的提交方法。
<
br
/>
2、事务协调器将联络事务中涉及的每个资源管理器,并通知它们准备提交事务(这是第一阶段的开始)。
<
br
/>
3、为 了以肯定的方式响应准备阶段,资源管理器必须将自己置于以下状态:确保能在被要求提交事务时提交事务,
或在被要求回滚事务时回滚事务。大多数资源管理器会将包含其计划更改的日记文件(或等效文件)写入持久存储区中。如果资源管理器无法准备事务,它会以一个
否定响应来回应事务协调器。
<
br
/>
4、事务协调器收集来自资源管理器的所有响应。
<
br
/>
5、在 第二阶段,事务协调器将事务的结果通知给每个资源管理器。如果任一资源管理器做出否定响应,则事务协调
器会将一个回滚命令发送给事务中涉及的所有资源管理 器。如果资源管理器都做出肯定响应,则事务协调器会指示所有的资源管理器提交事务。一旦通知资源管理
器提交,此后的事务就不能失败了。通过以肯定的方式响应第一阶段,每个资源管理器均已确保,如果以后通知它提交事务,则事务不会失败。
</
p
>
</
asp:Content
>
Sample.aspx.cs
using
System;
using
System.Collections;
using
System.Configuration;
using
System.Data;
using
System.Linq;
using
System.Web;
using
System.Web.Security;
using
System.Web.UI;
using
System.Web.UI.HtmlControls;
using
System.Web.UI.WebControls;
using
System.Web.UI.WebControls.WebParts;
using
System.Xml.Linq;
using
System.Threading;
public
partial
class
Transaction_Sample : System.Web.UI.Page
{
protected
void
Page_Load(
object
sender, EventArgs e)
{
}
protected
void
btnSubmit_Click(
object
sender, EventArgs e)
{
var proxyHello
=
new
TransactionSvc.Hello.HelloClient();
var proxyHi
=
new
TransactionSvc.Hi.HiClient();
var proxyResult
=
new
TransactionSvc.Result.ResultClient();
System.Transactions.TransactionOptions to
=
new
System.Transactions.TransactionOptions();
//
设置事务的超时时间
to.Timeout
=
new
TimeSpan(
0
,
0
,
30
);
//
设置事务的隔离级别
to.IsolationLevel
=
System.Transactions.IsolationLevel.Serializable;
using
(var ts
=
new
System.Transactions.TransactionScope())
{
try
{
proxyHello.WriteHello(
"
webabcd
"
);
proxyHello.Close();
proxyHi.WriteHi(
"
webabcd
"
);
proxyHi.Close();
ts.Complete();
lblErr.Text
=
"
OK
"
;
}
catch
(Exception ex)
{
lblErr.Text
=
ex.ToString();
}
}
GridView1.DataSource
=
proxyResult.GetResult();
GridView1.DataBind();
proxyHello.Close();
}
}
Web.config
<?
xml version="1.0"
?>
<
configuration
>
<
system.serviceModel
>
<
client
>
<!--
address - 服务地址
-->
<!--
binding - 通信方式
-->
<!--
contract - 服务契约
-->
<
endpoint
address
="http://localhost:3502/ServiceHost/Transaction/Hello.svc"
binding
="wsHttpBinding"
contract
="TransactionSvc.Hello.IHello"
bindingConfiguration
="TransactionBindingConfiguration"
/>
<
endpoint
address
="http://localhost:3502/ServiceHost/Transaction/Hi.svc"
binding
="wsHttpBinding"
contract
="TransactionSvc.Hi.IHi"
bindingConfiguration
="TransactionBindingConfiguration"
/>
<
endpoint
address
="http://localhost:3502/ServiceHost/Transaction/Result.svc"
binding
="basicHttpBinding"
contract
="TransactionSvc.Result.IResult"
/>
</
client
>
<
bindings
>
<
wsHttpBinding
>
<!--
transactionFlow - 指定该绑定是否应支持流事务
-->
<
binding
name
="TransactionBindingConfiguration"
transactionFlow
="true"
/>
</
wsHttpBinding
>
</
bindings
>
</
system.serviceModel
>
</
configuration
>
运行结果:
单击"btnSubmit
"
按钮后,可以发现,两个数据库插入操作,要么都执行,要么都不执行