介绍
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();
}
}
}
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();
}
}
}
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();
}
}
}
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 >
< 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 >
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();
}
}
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 >
< 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 " 按钮后,可以发现,两个数据库插入操作,要么都执行,要么都不执行