[WCF Transaction] 3. 事务投票
来源:互联网 发布:中国联通wap网络 编辑:程序博客网 时间:2024/05/17 08:03
我们知道事务是通过参与方进行投票(Voting)来决定 "提交(Complete)" 或者 "回滚(Rollback)"操作的。默认情况下,WCF 通过 OperationBehavior(TransactionAutoComplete = true)来完成投票动作。(TransactionAutoComplete = true 是缺省值,不需要显式声明。)
我们还是使用第 2 章的例子,将服务方法默认的 TransactionAutoComplete=true 改为 false,看看结果 。
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IService1
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void Test();
}
public class MyService1 : IService1
{
[OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=false)]
public void Test()
{
string connStr = "server=(local);uid=sa;pwd=sa;database=temp";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand("insert into [User] ([name]) values (@name)",
conn);
cmd.Parameters.Add(new SqlParameter("@name", "ZhangSan"));
cmd.ExecuteNonQuery();
}
}
}
// ---- Service2 -----
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IService2
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void Test();
}
public class MyService2 : IService2
{
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void Test()
{
string connStr = "server=(local);uid=sa;pwd=sa;database=temp";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand("insert into Account ([user], [money]) values (@user, @money)",
conn);
cmd.Parameters.Add(new SqlParameter("@user", "ZhangSan"));
cmd.Parameters.Add(new SqlParameter("@money", 100));
cmd.ExecuteNonQuery();
}
}
}
public class WcfTest
{
public static void Test()
{
// ---- Host -----
AppDomain.CreateDomain("Server").DoCallBack(delegate
{
NetTcpBinding bindingServer = new NetTcpBinding();
bindingServer.TransactionFlow = true;
ServiceHost host1 = new ServiceHost(typeof(MyService1), new Uri("net.tcp://localhost:8080"));
host1.AddServiceEndpoint(typeof(IService1), bindingServer, "");
host1.Open();
ServiceHost host2 = new ServiceHost(typeof(MyService2), new Uri("net.tcp://localhost:8081"));
host2.AddServiceEndpoint(typeof(IService2), bindingServer, "");
host2.Open();
});
// ---- Client -----
NetTcpBinding bindingClient = new NetTcpBinding();
bindingClient.TransactionFlow = true;
IService1 client1 = ChannelFactory<IService1>.CreateChannel(bindingClient,
new EndpointAddress("net.tcp://localhost:8080"));
IService2 client2 = ChannelFactory<IService2>.CreateChannel(bindingClient,
new EndpointAddress("net.tcp://localhost:8081"));
using (TransactionScope scope = new TransactionScope())
{
try
{
client1.Test();
client2.Test();
scope.Complete();
}
finally
{
(client1 as IDisposable).Dispose();
(client2 as IDisposable).Dispose();
}
}
}
}
运行结果表明事务无法提交,触发 TransactionAbortedException 异常,显示 "事务终止"。那么除了默认被称之为"声明投票(Declarative voting)" 的方式外,我们还能怎么做?OperationContext 有个SetTransactionComplete()方法,允许我们在代码中完成投票行为。这种投票方式更加灵活,便于我们在代码中做出更多的控制,被称之为 "显式投票(Explicitvoting)"。
在上面两个 Test() 方法的最后一行,添加 "OperationContext.Current.SetTransactionComplete();",再次运行,事务被正确提交。
[OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=false)]
public void Test()
{
// ...
OperationContext.Current.SetTransactionComplete();
}
...
接下来,我们设想另外一种情况。事务不由 Client 发起,在 Service1.Test() 调用Service2.Test(),那么事务会是个什么样子呢?Service1、Service2 的参数"OperationBehavior(TransactionScopeRequired = true)"决定了如果没有外界传入的环境事务,那么会自动创建一个根事务。所以 Service1.Test() 会创建一个根事务,而Service2.Test() 会参与这个事务。可问题在于 Service.Test() 中并没有显示调用Transaction.Complete,事务能被提交吗?
// ---- Service1 -----
[ServiceContract]
public interface IService1
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void Test();
}
public class MyService1 : IService1
{
[OperationBehavior(TransactionScopeRequired=true)]
public void Test()
{
string connStr = "server=(local);uid=sa;pwd=sa;database=temp";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand("insert into [User] ([name]) values (@name)",
conn);
cmd.Parameters.Add(new SqlParameter("@name", "ZhangSan"));
cmd.ExecuteNonQuery();
}
InvokeService2();
}
public void InvokeService2()
{
NetTcpBinding bindingClient = new NetTcpBinding();
bindingClient.TransactionFlow = true;
IService2 client2 = ChannelFactory<IService2>.CreateChannel(bindingClient,
new EndpointAddress("net.tcp://localhost:8081"));
using (client2 as IDisposable)
{
client2.Test();
}
}
}
// ---- Service2 -----
[ServiceContract]
public interface IService2
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void Test();
}
public class MyService2 : IService2
{
[OperationBehavior(TransactionScopeRequired = true)]
public void Test()
{
string connStr = "server=(local);uid=sa;pwd=sa;database=temp";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand("insert into Account ([user], [money]) values (@user, @money)",
conn);
cmd.Parameters.Add(new SqlParameter("@user", "ZhangSan"));
cmd.Parameters.Add(new SqlParameter("@money", 100));
cmd.ExecuteNonQuery();
}
}
}
public class WcfTest
{
public static void Test()
{
// ---- Host -----
AppDomain.CreateDomain("Server").DoCallBack(delegate
{
NetTcpBinding bindingServer = new NetTcpBinding();
bindingServer.TransactionFlow = true;
ServiceHost host1 = new ServiceHost(typeof(MyService1), new Uri("net.tcp://localhost:8080"));
host1.AddServiceEndpoint(typeof(IService1), bindingServer, "");
host1.Open();
ServiceHost host2 = new ServiceHost(typeof(MyService2), new Uri("net.tcp://localhost:8081"));
host2.AddServiceEndpoint(typeof(IService2), bindingServer, "");
host2.Open();
});
// ---- Client -----
NetTcpBinding bindingClient = new NetTcpBinding();
bindingClient.TransactionFlow = true;
IService1 client1 = ChannelFactory<IService1>.CreateChannel(bindingClient,
new EndpointAddress("net.tcp://localhost:8080"));
try
{
client1.Test();
}
finally
{
(client1 as IDisposable).Dispose();
}
}
}
运行结果表明,事务被正确提交。看来这和客户端使用 TransactionScope 必须显式调用 Complete() 有所不同。同样,如果将Service2.Test() 设为 TransactionAutoComplete=false,在不调用"OperationContext.Current.SetTransactionComplete();" 的情况下,也会触发事务失败异常。
- [WCF Transaction] 3. 事务投票
- [WCF Transaction] 2. 事务演示
- 化零为整WCF(14) - 事务(Transaction)
- 化零为整WCF(14) - 事务(Transaction)
- 化零为整WCF(14) - 事务(Transaction)
- [WCF Transaction] 4. 事务与会话
- WCF从理论到实践(13):事务投票
- WCF分布式开发步步为赢(12):WCF事务机制(Transaction)和分布式事务编程
- Transaction 事务
- Transaction-事务
- 事务Transaction
- Transaction事务
- 事务(Transaction)
- Transaction 事务
- 事务(Transaction)
- 事务Transaction
- [事务] -- 事务(Transaction)
- WCF分布式开发常见错误(16):The Transaction has aborted,事务已经被中断
- [WCF Security] 3. X509 身份验证
- t:dataScroller的使用-使用binding的时候会出现的问题
- [WCF Security] 4. 用户名/密码身份验证
- [WCF Transaction] 1. 基本概念
- [WCF Transaction] 2. 事务演示
- [WCF Transaction] 3. 事务投票
- 基于消息驱动的面向对象通用C/S应用框架(一)
- 基于消息驱动的面向对象通用C/S应用框架(二)
- [WCF Transaction] 4. 事务与会话
- [WCF MSMQ] 1. 基本应用
- [WCF MSMQ] 2. 队列与事务
- 基于消息驱动的面向对象通用C/S应用框架(三)
- WCF - 只读属性
- 基于消息驱动的面向对象通用C/S应用框架(四)