你的位置:首页 > ASP.net教程

[ASP.net教程]如何创建一个简单的基于OleTx协议的WCF事务实例


    由于工作需要,最近研究了一下WCF分布式事务方面的技术,自己做了一个基于OleTx协议的WCF事务实例,供大家参考。

    一、实例背景

    本实例以大家熟悉的银行转账业务为背景,在几台不同的机器上部署一套基于OleTx协议的WCF分布式服务。

    本例需要A、B、C、D四台计算机,其中A表示SqlServer服务器,B部署取款服务,C部署存款服务,D部署客户端程序(调用存款和取款服务),D同时直接A进行日志写入。一次转账操作分为三个部分:存款、取款、写日志,这三部分组成一个转账事务,要么全部成功,要么全部失败。

    二、WCF服务端代码

    创建一个WCF服务项目OleTxService。

    1、契约

    取款服务契约如下:

 1 using System.Net.Security; 2 using System.ServiceModel; 3 using OleTxService.Models; 4  5 namespace OleTxService 6 { 7   /// <summary> 8   /// 取款服务接口 9   /// </summary>10   [ServiceContract(SessionMode = SessionMode.Required)]11   public interface IWithDrawService12   {13     /// <summary>14     /// 取款操作15     /// </summary>16     /// <param name="accountId">取款账号</param>17     /// <param name="amount">取款金额</param>18     [OperationContract]19     [TransactionFlow(TransactionFlowOption.Mandatory)]20     [FaultContract(typeof(MyFault), ProtectionLevel = ProtectionLevel.None)]21     void Withdraw(string accountId, double amount);22   }23 }

View Code

    存款服务契约如下:

 1 using System.Net.Security; 2 using System.ServiceModel; 3 using OleTxService.Models; 4  5 namespace OleTxService 6 { 7   /// <summary> 8   /// 存款服务接口 9   /// </summary>10   [ServiceContract(SessionMode = SessionMode.Required)]11   public interface IDepositService12   {13     /// <summary>14     /// 存款操作15     /// </summary>16     /// <param name="accountId">存款账号</param>17     /// <param name="amount">存款金额</param>18     [OperationContract]19     [FaultContract(typeof(MyFault), ProtectionLevel = ProtectionLevel.None)]20     [TransactionFlow(TransactionFlowOption.Mandatory)]21     void Deposit(string accountId, double amount);22   }23 }

View Code

    由于后面要设置事务操作行为的TransactionAutoComplete属性为false,所以要求其中契约的Session要设置为SessionMode.Required。

    在操作上要增加一个TransactionFlow特性,允许事务在客户端到服务端之间流转。

    2、服务实现

    取款服务如下:

 1 using System; 2 using System.Configuration; 3 using System.Data; 4 using System.Data.SqlClient; 5 using System.ServiceModel; 6 using OleTxService.Helper; 7 using OleTxService.Models; 8  9 namespace OleTxService10 {11   /// <summary>12   /// 取款服务13   /// </summary>14   public class WithDrawService : IWithDrawService15   {16     /// <summary>17     /// 取款操作18     /// </summary>19     /// <param name="accountId">取款账号</param>20     /// <param name="amount">取款金额</param>21     [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]22     public void Withdraw(string accountId, double amount)23     {24       string connString = ConfigurationManager.ConnectionStrings["MyBank"].ConnectionString;25       try26       {27         var _parms = new SqlParameter[]28         {29           SQLServerHelper.BuildInParameter("id", SqlDbType.VarChar, accountId),30           SQLServerHelper.BuildInParameter("amount", SqlDbType.Float, amount)31         };32         SQLServerHelper.ExecuteNonQuery(connString, CommandType.StoredProcedure, "P_WITHDRAW", _parms);33         OperationContext.Current.SetTransactionComplete();34       }35       catch (Exception ex)36       {37         throw new FaultException<MyFault>(new MyFault(string.Format("取款异常:{0}", ex.Message)), "取款出错");38       }39     }40   }41 }

View Code

    存款服务如下:

 1 using System; 2 using System.Configuration; 3 using System.Data; 4 using System.Data.SqlClient; 5 using System.ServiceModel; 6 using OleTxService.Helper; 7 using OleTxService.Models; 8  9 namespace OleTxService10 {11   /// <summary>12   /// 存款服务13   /// </summary>14   public class DepositService : IDepositService15   {16     /// <summary>17     /// 存款操作18     /// </summary>19     /// <param name="accountId">存款账号</param>20     /// <param name="amount">存款金额</param>21     [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]22     public void Deposit(string accountId, double amount)23     {24       string connString = ConfigurationManager.ConnectionStrings["MyBank"].ConnectionString;25       try26       {27         var _parms = new SqlParameter[]28         {29           SQLServerHelper.BuildInParameter("id", SqlDbType.VarChar, accountId),30           SQLServerHelper.BuildInParameter("amount", SqlDbType.Float, amount)31         };32         SQLServerHelper.ExecuteNonQuery(connString, CommandType.StoredProcedure, "P_DEPOSIT", _parms);33         OperationContext.Current.SetTransactionComplete();34       }35       catch (Exception ex)36       {37         throw new FaultException<MyFault>(new MyFault(string.Format("存款异常:{0}", ex.Message)), "存款出错");38       }39     }40   }41 }

View Code

    其中,取款和存款的具体代码未贴出,读者可以自己实现。具体操作:从银行账户表的一个账号(一行数据)减少金额,另一个账号增加金额。

    注意,在服务操作上增加了一个操作行为:[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]。TransactionScopeRequired=true表示该服务操作需要在一个显示的TransactionScope中。TransactionAutoComplete=false表示必须手工完成该事务,其方法是在要完成事务的地方写上:OperationContext.Current.SetTransactionComplete();

    3、配置WCF服务

    在配置文件中配置这两个服务,有关WCF服务的配置代码如下:

 <system.serviceModel>  <bindings>   <netTcpBinding>    <binding name="transactionBinding" transactionFlow="true" transactionProtocol="OleTransactions" />   </netTcpBinding>  </bindings>  <services>   <service name="OleTxService.DepositService">    <endpoint address="deposit" binding="netTcpBinding" bindingConfiguration="transactionBinding"     contract="OleTxService.IDepositService" />   </service>   <service name="OleTxService.WithDrawService">    <endpoint address="withdraw" binding="netTcpBinding" bindingConfiguration="transactionBinding"     contract="OleTxService.IWithDrawService" />   </service>  </services>  <behaviors>   <serviceBehaviors>    <behavior>     <!-- To avoid disclosing metadata information, set the values below to false before deployment -->     <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>     <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->     <serviceDebug includeExceptionDetailInFaults="false"/>    </behavior>   </serviceBehaviors>  </behaviors>  <protocolMapping>    <add binding="basicHttpsBinding" scheme="https" />  </protocolMapping>    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel>

View Code

    我们将服务配置成netTcpBinding,并在绑定配置中加上允许事务流转、指定事务协议的配置项:

  <bindings>   <netTcpBinding>    <binding name="transactionBinding" transactionFlow="true" transactionProtocol="OleTransactions" />   </netTcpBinding>  </bindings>

View Code

    三、在IIS中部署netTcpBinding的WCF服务

    由于我们的服务是采用netTcpBinding,在IIS中寄宿时需要有些注意的地方。

    1、部署网站

    在IIS中新增一个网站oletransaction,用来部署本例中的服务。在本例中,服务终结点中的地址配置的是相对地址,也可以为空。

    2、编辑绑定

    在IIS中寄宿非HTTP协议的WCF服务时,需要手动为网站编辑相应的绑定。在本例中,需要为服务增加一个net.tcp绑定,并在其中指定绑定端口。

    在IIS中选中网站,右击“编辑绑定(Edit Bindings...)”,在弹出的“站点绑定(Site Bindings)”对话框中添加一个net.tcp绑定。

       

    其中,绑定信息“8060:*”表示设置TCP的监听端口为8060。

    3、添加net.tcp协议支持

    需要手动在网站中添加对net.tcp协议的支持。

    选中网站,点击“高级设置(Advanced Setting...)”。

    在弹出的高级设置窗口中找到“启用的协议(Enabled Protocols)”,添加net.tcp,协议之间以逗号分隔。

    4、浏览服务

    在完成编辑绑定、添加协议支持后,即可浏览服务:

    如何不能访问,请检查防火墙是否关闭,Net Tcp监听服务是否打开。

    照此方式在B、C服务器中分别部署取款服务、存款服务。

    四、WCF客户端代码

    1、引用服务

    根据服务的WSDL地址分别引用取款服务、取款服务。

    2、编写转账代码

    转账的总体代码如下:

        using (var scope = new TransactionScope())        {          try          {            Transfer(fromAccountId, toAccountId, amount);            var logContent = string.Format("完成一条转账操作,转出账号:{0},转入账号:{1},转账金额:{2}", fromAccountId, toAccountId,              amount);            WriteLog(logContent, remark);            scope.Complete();            Console.WriteLine("转账成功!");          }          catch (FaultException<withdraw.MyFault> ex)          {            Console.WriteLine("转账失败:{0},错误详情:{1}", ex.Message, ex.Detail.ErrorMessage);           }          catch (FaultException<deposit.MyFault> ex)          {            Console.WriteLine("转账失败:{0},错误详情:{1}", ex.Message, ex.Detail.ErrorMessage);           }          catch (Exception ex)          {            Console.WriteLine("转账失败:{0}", ex.Message);          }        }

View Code

    其中转账方法Transfer定义如下:

    /// <summary>    /// 转账操作    /// </summary>    /// <param name="fromAccountId">转出账号</param>    /// <param name="toAccountId">转入账号</param>    /// <param name="amount">转账金额</param>    static void Transfer(string fromAccountId, string toAccountId, double amount)    {      withdraw.WithDrawServiceClient clientWithdraw = new WithDrawServiceClient("NetTcpBinding_IWithDrawService");      clientWithdraw.Withdraw(fromAccountId, amount);      deposit.DepositServiceClient clientDeposit = new DepositServiceClient("NetTcpBinding_IDepositService");      clientDeposit.Deposit(toAccountId, amount);    }

View Code

    写日志的代码如下:

    /// <summary>    /// 记录日志    /// </summary>    /// <param name="content">日志内容</param>    /// <param name="remark">备注</param>    static void WriteLog(string content, string remark)    {      var sql = ConfigurationManager.AppSettings["InsertBankRecord"];      var _params = new SqlParameter[]      {        SQLServerHelper.BuildInParameter("ID", SqlDbType.UniqueIdentifier, Guid.NewGuid()),        SQLServerHelper.BuildInParameter("Content", SqlDbType.NVarChar, content),        SQLServerHelper.BuildInParameter("Remark", SqlDbType.NVarChar, remark ?? string.Empty),        SQLServerHelper.BuildInParameter("UpdateTime", SqlDbType.DateTime, DateTime.Now)      };      SQLServerHelper.ExecuteNonQuery(_myConnString, CommandType.Text, sql, _params);    }

View Code

    在转账的总体代码中,将调用取款服务、存款服务、写日志这三个操作放到一个TransactionScope变量scope中。只有当三个操作本身正常完成,并且调用scope.Complete()后,事务成功,否则事务失败。

    3、部署客户端

    将客户端部署到D计算机,并配置好服务地址、数据库连接串。

    五、配置MSDTC

    由于本例采用的是基于OleTx协议的WCF事务,用到了微软的MSDTC服务,因此,需要配置MSDTC服务。

    1、配置MSDTC

    在“管理工具(Administrative Tools)”中打开“组件服务(Component Services)”。

    找到我的计算机节点:“组件服务(Component Services)”=>“计算机(Computers)”=>“我的计算机(My Computer)”,右键点击“属性(Properties)”。

    在“MSDTC”选项卡中,勾选“使用本地协调器(Use local coordinator)”选项。

    2、配置Local DTC

    还需要配置“本地DTC(Local DTC)”。

    在组件服务中找到本地DTC节点:组件服务(Component Services)=>计算机(Computers)=>我的计算机(My Computer)=>分布式事务协调器(Distributed Transaction Coordinator),右击“属性(Properties)”。

    在弹出的本地DTC属性窗口中,点击“安全(Security)”选项卡,配置成下面的值。

    六、验证事务

    部署好整个WCF分布式事务系统后,运行程序,验证事务正确性:

  • 正常输入情况下应该完成整个事务
  • 调用服务错误情况下应该回滚整个事务
  • 本地写日志出错情况下应该回滚整个事务

    七、总结

    本文完成了一个基于OleTx协议的WCF分布式事务的代码编写和系统部署的过程。总共有三大要点:

  • WCF事务编程,主要注意契约和操作行为中的一些关于事务的特性。
  • 在IIS中部署基于netTcpBinding的WCF服务,主要关注编辑站点绑定、添加net.tcp协议支持。
  • 配置MSDTC和本地DTC,这是部署WCF分布式事务的必要条件。



四川旅游线路大全什么时候去四川旅游最好四川跟团旅游报价多少四川旅游必去的地方是四川旅游必去景点排名五一去哪玩?_五一去天津旅游去哪好? 五一看广东薰衣草哪里最多最好看?广东薰衣草几时开? 广东薰衣草五一开了吗?五一去看广东薰衣草好看吗? 广州到沙湾古镇坐几号地铁?广州到沙湾古镇在哪坐地铁? 重走女王访昆路线 看女王曾经路过的昆明风景 大云雾山地址?云浮大云雾山在哪里? 敦煌莫高窟 绚丽的历史文化瑰宝 2015珠海元宵节晚会在哪里?珠海元宵节灯会要门票吗? 中山逍遥谷一日游?五桂山逍遥谷怎么走? 中山逍遥谷学生票多少钱?五桂山逍遥谷学生门票价格? 中山逍遥谷简介?五桂山逍遥谷介绍? 五桂山逍遥谷烧烤价格?逍遥谷门票多少钱? 泰国普吉岛好玩吗?泰国普吉岛旅游攻略 迪拜十月份天气如何?穿什么衣服好? 马尔代夫十月份天气如何?穿什么衣服好? 国庆去泰国玩出入境要注意什么? IDT72V3640L7-5BB8 Datasheet IDT72V3640L7-5BB8 Datasheet IDT5V927PGGI8 Datasheet IDT5V927PGGI8 Datasheet IDT71V3558S200BQI Datasheet IDT71V3558S200BQI Datasheet 大兴安岭到香港二天游 大兴安岭到香港二天游 大兴安岭到香港二天游 黑龙江跟团香港两天一晚游 黑龙江跟团香港两天一晚游 黑龙江跟团香港两天一晚游 四平去香港三天两晚游 四平去香港三天两晚游 四平去香港三天两晚游