化零为整WCF(14) 事务(Transaction)


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

作者:Angelo Lee
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
原文地址:https://www.cnblogs.com/yefengmeander/p/2887776.html