[WCF 学习笔记] 13. 分布事务

WCF 对分布事务提供了良好的支持,这使得我们可以协调多个服务之间的数据完整性。通过 TransactionFlowAttribute、ServiceBehaviorAttribute 和 OperationBehaviorAttribute 这三个特性,我们可以很好地控制事务的相关细节。

TransactionFlowAttribute 的构造参数 "TransactionFlowOption transactions" 允许我们在强制事务(Mandatory)、允许参与事务(Allowed)和禁止事务(NotAllowed)间进行选择。

ServiceBehaviorAttribute 提供了多个属性参与事务控制。TransactionAutoCompleteOnSessionClose 指示当会话(Session)结束时是否自动提交事务(Complete); ReleaseServiceInstanceOnTransactionComplete 指示事务提交后是否释放服务实例对象; TransactionIsolationLevel 用于设置事务隔离方式(isolation level); TransactionTimeout 用于设置事务超时时间。

OperationBehaviorAttribute 的 TransactionScopeRequired 属性是 WCF 分布事务所必需使用的,它表明服务方法必须在事务范围(transaction scope)内执行。如果不添加该标记,则意味着服务方法不参与到事务中。TransactionAutoComplete 指示方法正常结束后自动提交事务。

上面这些特性属性的使用有些特殊的细节要求,本文只是做个简要的说明,有关更多内容,请参考 MSDN 帮助文档。

演示

下面的代码中,我们使用新的程序域来模拟两个服务,客户端通过 TransactionScope 来完成分布事务调用。

步骤:

1. 使用 TransactionFlowAttribute 对契约方法进行标注,启用事务。
2. 使用 OperationBehaviorAttribute 对服务方法进行标注,使用事务。
3. 将所有 Binding.TransactionFlow 设置为 true。
[ServiceContract]
public interface IContract
{
  [OperationContract]
  [TransactionFlow(TransactionFlowOption.Mandatory)]
  void Test();
}

public class MyService : IContract
{
  [OperationBehavior(TransactionScopeRequired=true)]
  public void Test()
  {
    string connStr = "Data Source=(local);Initial Catalog=tempdb;Integrated Security=True";

    using (SqlConnection conn = new SqlConnection(connStr))
    {
      conn.Open();

      SqlCommand cmd = conn.CreateCommand();
      cmd.CommandText = String.Format("insert into table{0} ([name]) values ('name1')", 
        AppDomain.CurrentDomain.FriendlyName == "Server1" ? "1" : "2");

      cmd.ExecuteNonQuery();
    }
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server1").DoCallBack(delegate
    {
      WSHttpBinding binding = new WSHttpBinding();
      binding.TransactionFlow = true;

      ServiceHost host = new ServiceHost(typeof(MyService));
      host.AddServiceEndpoint(typeof(IContract), binding, "http://localhost:8080/myservice");
      host.Open();
    });

    AppDomain.CreateDomain("Server2").DoCallBack(delegate
    {
      WSHttpBinding binding = new WSHttpBinding();
      binding.TransactionFlow = true;

      ServiceHost host = new ServiceHost(typeof(MyService));
      host.AddServiceEndpoint(typeof(IContract), binding, "http://localhost:8081/myservice");
      host.Open();
    });

    WSHttpBinding bindingx = new WSHttpBinding();
    bindingx.TransactionFlow = true;

    ChannelFactory<IContract> factory = new ChannelFactory<IContract>(bindingx, 
      "http://localhost:8080/myservice");
    IContract client = factory.CreateChannel();

    ChannelFactory<IContract> factory2 = new ChannelFactory<IContract>(bindingx, 
      "http://localhost:8081/myservice");
    IContract client2 = factory2.CreateChannel();

    try
    {
      using (TransactionScope scope = new TransactionScope())
      {
        client.Test();
        //throw new Exception();
        client2.Test();

        scope.Complete();
      }
    }
    catch
    {
    }
    
    factory.Close();
    factory2.Close();
  }
}

posted on 2008-11-06 13:22  topzengyi  阅读(217)  评论(1)    收藏  举报

导航