[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。
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();
}
}
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();
}
}
浙公网安备 33010602011771号