WCF 分佈式事務-應用

  WCF 支持分佈式事務,也就是說事務可以跨越服務、進程、機器邊界,在多個服務和客戶端間存在.那麼在WCF中如何使用分佈式事務呢?

下面我們以一個例子來演示在wcf中使用分佈式事務。

爲了便於測試,我們在建立一個臨時表用於測試。表名爲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");


發佈了64 篇原創文章 · 獲贊 12 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章