[源码下载]
化零为整WCF(14) - 事务(Transaction)
作者:webabcd
介绍
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"按钮后,可以发现,两个数据库插入操作,要么都执行,要么都不执行
OK
[源码下载]