zoukankan      html  css  js  c++  java
  • 在WCF中如何使用分布式事务

        WCF 支持分布式事务,也就是说事务可以跨越服务、进程、机器边界,在多个服务和客户端间存在.那么在WCF中如何使用分布式事务呢?


     

    下面我们以一个例子来演示在wcf中使用分布式事务。下图是这个demo的整个solution:

    为了便于测试,我们在建立一个临时表用于测试。表名为MGender.表中有两个字段:GenderCode,char(1),GenderDesc varchar(20).

    在这个solution中WCFTrasactionServcies是contract 和Service。它是一个类库项目,在这里为了方便我们将interfact和实现interfact写在一个cs文件里,当然这种方法是不被推荐的。代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.Data.SqlClient;
    namespace WCFTrasactionServices
    {
        [ServiceContract(SessionMode=SessionMode.Required)]
        public interface IGenderInsert1
        {
            [OperationContract]
            [TransactionFlow(TransactionFlowOption.Allowed)]
            void insertGerder1(string genderID, string genderDesc);
        }

        [ServiceContract(SessionMode=SessionMode.Required)]
        public interface IGenderInsert2
        {
            [OperationContract]
            [TransactionFlow(TransactionFlowOption.Allowed)]
            void insertGerder2(string genderID, string genderDesc);
        }

        public class TestService1 : IGenderInsert1
        {

            #region IGenderInsert1 Members
            [OperationBehavior(TransactionScopeRequired = true)]
            public void insertGerder1(string genderID, string genderDesc)
            {
                using (SqlConnection conn = new SqlConnection("Data Source=localhost;DataBase=Test;integrated security=true"))
                {
                    conn.Open();
                    SqlCommand cmd = new SqlCommand();
                    cmd.CommandText = "insert into MGender (GenderCode,GenderDesc) values (@GenderCode,@GenderDesc)";
                    cmd.Parameters.Add(new SqlParameter("@GenderCode", genderID));
                    cmd.Parameters.Add(new SqlParameter("@GenderDesc", genderDesc));
                    cmd.Connection = conn;
                    cmd.ExecuteNonQuery();

                }
            }

            #endregion
        }

        public class TestService2 : IGenderInsert2
        {

            #region IGenderInsert2 Members
            [OperationBehavior(TransactionScopeRequired = true)]
            public void insertGerder2(string genderID, string genderDesc)
            {
                using (SqlConnection conn = new SqlConnection("Data Source=localhost;DataBase=Test;integrated security=true"))
                {
                    conn.Open();
                    SqlCommand cmd = new SqlCommand();
                    cmd.CommandText = "insert into MGender (GenderCode,GenderDesc) values (@GenderCode,@GenderDesc)";
                    cmd.Parameters.Add(new SqlParameter("@GenderCode", genderID));
                    cmd.Parameters.Add(new SqlParameter("@GenderDesc", genderDesc));
                    cmd.Connection = conn;
                    cmd.ExecuteNonQuery();
                }
            }
            #endregion
        }
    }
    TransactionFlowAttribute 只能用于服务方法(Operation/Method)上,它允许我们进行不同的事务参与设置。有一点要注意,我们不能为 IsOneWay=true 的服务设置事务支持。

    TransactionFlowOption.NotAllowed: 不参与任何事务。(默认值)

    TransactionFlowOption.Allowed: 允许参与事务。也就是说,如果调用方(客户端)和服务Binding启用了事务,则参与。

    TransactionFlowOption.Mandatory: 强制启用事务。调用方(客户端)和服务 Binding 必须启用事务才能调用本服务。

    WCFTrasactionHost是host,为了让WCF支持分布式事务,我们要修改Binding的一些属性。只有 TCP-、 IPC- 以及 WS-related 等 Binding 支持事务。缺省情况下,这些 Binding 并不会参与事务,需要我们显示将 TransactionFlow 属性设置为 true 才行。
    我们可以通过配置文件来设置

    <bindings>
      <netTcpBinding>
        <binding name = "Transactional" transactionFlow = "true" />
      </netTcpBinding>
    </bindings>
    当然也可以使用代码:
    NetTcpBinding tcpBinding = new NetTcpBinding( );
    tcpBinding.TransactionFlow = true;
    WCFTrasactionHost是一个控制台应用程序,它的代码如下:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    namespace WCFTrasactionHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                ServiceHost host1 = new ServiceHost(typeof(WCFTrasactionServices.TestService1));
                host1.Open();
                ServiceHost host2 = new ServiceHost(typeof(WCFTrasactionServices.TestService2));
                host2.Open();
                Console.WriteLine("endpoint is listenning");
                Console.ReadKey();
            }
        }
    }
    WCFTrasactionHost配置文件:
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <services>
          <service name ="WCFTrasactionServices.TestService1" behaviorConfiguration="TestBehavior1">
            <endpoint binding="netTcpBinding" bindingConfiguration="TransactionalTCP" contract="WCFTrasactionServices.IGenderInsert1" address="net.tcp://localhost/Test1"></endpoint>       
          </service>
          <service name ="WCFTrasactionServices.TestService2" behaviorConfiguration="TestBehavior2">
            <endpoint binding="netTcpBinding" contract="WCFTrasactionServices.IGenderInsert2" bindingConfiguration="TransactionalTCP" address="net.tcp://localhost/Test2"></endpoint>
          </service>
        </services>
        <bindings>
          <netTcpBinding>
            <binding name="TransactionalTCP" transactionFlow="true"></binding>
          </netTcpBinding>
        </bindings>
        <behaviors>
          <serviceBehaviors>
            <behavior name="TestBehavior1">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost/Test1"/>
            </behavior>
            <behavior name="TestBehavior2">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost/Test2"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>


    WCFTrasactionClient:是client.我们使用svcutil 工具生成客户端代码,生成两个cs文件,TestService1.cs和TestService2.cs,当然我们也可以手工写客户端类。

    WCFTrasactionClient代码:

       class Program
        {
            static void Main(string[] args)
            {
                GenderInsert1Client test1 = new GenderInsert1Client();
                GenderInsert2Client test2 = new GenderInsert2Client();
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        test1.insertGerder1("M", "Male");
                        test2.insertGerder2("F"Female");
                        scope.Complete();
                        Console.WriteLine("Client has called the services.");                   
                    }
                    catch
                    { //do noting
                    }
                    finally
                    {
                        //(test1 as IDisposable).Dispose();
                        //(test2 as IDisposable).Dispose();
                    }

                }
            }

    这样我们就可以成功的往表MGender中插入两条数据。如果我们修改 :

      test1.insertGerder1("M", "Male"); test2.insertGerder2("F"Female");修改为 test1.insertGerder1("M", "Male"); test2.insertGerder2("FF"Female");

    这样就会有异常,因为FF是两位字符,而GenderCode是char1型的,所以事务不成功,但第一条数据也成功插入,所以WCF会自动回滚第一插入的数据 test1.insertGerder1("M", "Male");

  • 相关阅读:
    Nacos配置失败(java.lang.IllegalStateException: failed to req API:/nacos/v1/ns/instance after all server)
    数据库事务
    Consider defining a bean of type 'redis.clients.jedis.JedisPool' in your configuration.
    Mybatis+SpringBoot 项目All elements are null
    docker安装Sentinel
    docker安装nacos
    Docker 配置 Seata 集成 Nacos
    mybatis转义反斜杠_MyBatis Plus like模糊查询特殊字符_、、%
    在linux上配置Maven环境变量
    spring cache 学习——整合 redis 实现声明式缓存配置
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/1793238.html
Copyright © 2011-2022 走看看