spring.net nhibernate 分布布式事务(下)

本文介绍如何使用Spring.NET和WCF实现跨应用的分布式事务管理,包括启动MSDTC服务、配置服务契约及宿主,以及客户端事务处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘自: http://www.cnblogs.com/GoodHelper/archive/2010/07/30/SpringNetDistributedTransaction2.html

Spring.NET实用技巧4——NHibernate分布式事务(下)

 

  

  上篇,我们已实现了在同一应用程序下的分布式事务——即多Dao层+同Service层,每个Dao对应一个数据库,一个Service调用多个Dao。但是在一些特定的子系统较多的项目中,开发人员是无法访问到某个子系统的数据库,这就意味着不能通过增加Dao层来实现分布式事务。正如一个银行的软件系统,记录了客户的账户信息和存款金额,北京的分公司和上海的分公司分别有自己的数据库和软件系统。现在,要实现北京的系统向上海的系统转账,然而各自作为开发人员来说,没有足够的权限去访问对方的数据库,但是可以提供Web Service的方式去访问其系统服务。这样,我们就需要实现基于Web Service的分布式事务。

  实现基于Web Service的分布式事务的方法比较多,可以通过.NET企业服务的方式。但是为了更好的实现,我们选择WCF作为一个分布式应用程序框架。WCF在实现分布式事务中有它的优越之处。其思路在于启动MSDTC服务,将客户端的事务以流的方式传递到服务器端,在服务器端执行通过时,客户端再提交事务,相反则回滚事务。

  我们模仿上篇的场景做一个demo,并使用上篇的Dao和Domain。

 

  

  一、启动MSDTC服务。

 

  二、Service层

  ①.Customer

  

CustomerManager
    public interface ICustomerManager     {         CustomerInfo Get( object id);
       
object Save(CustomerInfo entity);
       
void Update(CustomerInfo entity);     }
   
public class CustomerManager : ICustomerManager     {         private ICustomerDao Dao { get ; set ; }
       
public CustomerInfo Get( object id)         {             return Dao.Get(id);         }
       
public object Save(CustomerInfo entity)         {             return Dao.Save(entity);         }
       
public void Update(CustomerInfo entity)         {             if (entity.Money > 3000 )             {                 throw new Exception( " 订金上限 " );             }             Dao.Update(entity);         }     }

 

  

 

Service.xml
<? xml version="1.0" encoding="utf-8" ?> < objects xmlns ="http://www.springframework.net" >
 
< object id ="transactionManager"         type ="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate21" >     < property name ="DbProvider" ref ="DbProvider" />     < property name ="SessionFactory" ref ="NHibernateSessionFactory" />   </ object >
 
< object id ="transactionInterceptor" type ="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data" >     < property name ="TransactionManager" ref ="transactionManager" />     < property name ="TransactionAttributeSource" >       < object type ="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data" />     </ property >   </ object >
 
< object id ="BaseTransactionManager"   type ="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data" abstract ="true" >     < property name ="PlatformTransactionManager" ref ="transactionManager" />     < property name ="TransactionAttributes" >       < name-values >           < add key ="*" value ="PROPAGATION_REQUIRED" />       </ name-values >     </ property >   </ object >
 
< object id ="Customer.CustomerManager" parent ="BaseTransactionManager" >     < property name ="Target" >       < object type ="Customer.Service.Implement.CustomerManager, Customer.Service" >         < property name ="Dao" ref ="Customer.CustomerDao" />       </ object >     </ property >   </ object >
</ objects >

 

 

  ②.Order

  

OrderManager
    public interface IOrderManager     {         object Save(OrderInfo entity);     }
   
public class OrderManager : IOrderManager     {         public IOrderDao Dao { get ; set ; }
       
public object Save(OrderInfo entity)         {             return Dao.Save(entity);         }     }

 

 

Service.xml
<? xml version="1.0" encoding="utf-8" ?> < objects xmlns ="http://www.springframework.net" >
 
< object id ="transactionManager"         type ="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate21" >     < property name ="DbProvider" ref ="DbProvider" />     < property name ="SessionFactory" ref ="NHibernateSessionFactory" />   </ object >
 
< object id ="transactionInterceptor" type ="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data" >     < property name ="TransactionManager" ref ="transactionManager" />     < property name ="TransactionAttributeSource" >       < object type ="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data" />     </ property >   </ object >
 
< object id ="BaseTransactionManager"   type ="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data" abstract ="true" >     < property name ="PlatformTransactionManager" ref ="transactionManager" />     < property name ="TransactionAttributes" >       < name-values >           < add key ="*" value ="PROPAGATION_REQUIRED" />       </ name-values >     </ property >   </ object >
 
< object id ="Order.OrderManager" parent ="BaseTransactionManager" >     < property name ="Target" >       < object type ="Order.Service.Implement.OrderManager, Order.Service" >         < property name ="Dao" ref ="Order.OrderDao" />       </ object >     </ property >   </ object >
</ objects >

 

  三、服务契约和Host。

  1、契约

  作为服务契约,需要启用Session,并且设置TransactionFlowOption的等级为Allowed或Mandatory来接收客户端事务流。

  作为契约的实现部分,需要设置TransactionScopeRequired为true来启用事务作用域。

 

  ①.Customer

 

CustomerContract
    [ServiceContract(SessionMode = SessionMode.Required)]     public interface ICustomerContract     {         [OperationContract]         [TransactionFlow(TransactionFlowOption.Allowed)]         CustomerInfo Get( object id);
        [OperationContract]         [TransactionFlow(TransactionFlowOption.Allowed)]        
object Save(CustomerInfo entity);
        [OperationContract]         [TransactionFlow(TransactionFlowOption.Allowed)]        
void Update(CustomerInfo entity);     }
    [AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Required)]     public class CustomerServer : ICustomerContract     {         public ICustomerManager Manager { get ; set ; }
        [OperationBehavior(TransactionScopeRequired
= true )]         public CustomerInfo Get( object id)         {             return Manager.Get(id);         }
        [OperationBehavior(TransactionScopeRequired
= true )]         public object Save(CustomerInfo entity)         {
           
return Manager.Save(entity);         }
        [OperationBehavior(TransactionScopeRequired
= true )]         public void Update(CustomerInfo entity)         {             Manager.Update(entity);         }

 

 

  ②.Order

  

IOrderContract
    [ServiceContract(SessionMode = SessionMode.Required)]     public interface IOrderContract     {         [OperationContract]         [TransactionFlow(TransactionFlowOption.Allowed)]         object Save(OrderInfo entity);     }
   [AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Required)]     public class OrderServer : IOrderContract     {         public IOrderManager Manager { get ; set ; }
        [OperationBehavior(TransactionScopeRequired
= true )]         public object Save(OrderInfo entity)         {             return Manager.Save(entity);         }     }

 

 

 

  2、配置

  然而,Spring.NET针对NHibernate的Session管理使用的是OSIV模式(Open Session In View),即使用httpModule去拦截HTTP请求,在每次请求开始时打开Session作用域(SessionScope),最后在请求结束后关闭SessionScope。这样一来,在客户端每请求一次时都会打开SessionScope,在请求结束会关闭SessionScope,然后当请求结束后再去处理分布式就会提示“无法使用已释放对象”的错误。所以说,OSIV是无法正常管理分布式事务的。出于上述原因,我们决定在Global.asax的配置,在Session(这里的Session是ASP.NET中的Session)启动时候打开SessionScope,在Session结束时关闭SessionScope。这样分布式事务就会与SessionScope同步了。

  最后,在配置appSettings节点增加     <add key="Spring.Data.NHibernate.Support.SessionScope.SessionFactoryObjectName" value="NHibernateSessionFactory"/>

  另外配置WCF的binding时需要选择一种支持Session的binding(如wsHttpBinding)并且将binding中的transactionFlow属性设置为true。

  

Global.asax
    public class Global : System.Web.HttpApplication     {
       
protected void Application_Start( object sender, EventArgs e)         {             log4net.Config.XmlConfigurator.Configure();         }
       
protected void Session_Start( object sender, EventArgs e)         {             SessionScope sessionScope = new SessionScope( " appSettings " , typeof (SessionScope), false );             sessionScope.Open();             HttpContext.Current.Session[ " SessionScope " ] = sessionScope;         }
               
protected void Session_End( object sender, EventArgs e)         {             SessionScope sessionScope = HttpContext.Current.Session[ " SessionScope " ] as SessionScope;             if (sessionScope != null )             {                 sessionScope.Close();             }         }
    }

 

 

  ①.Customer

 

Web.config
<? xml version="1.0" encoding="utf-8" ?> < configuration >
 
..............
<!-- spring配置 -->   < spring xmlns ="http://www.springframework.net" >     < parsers >       < parser type ="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />       < parser type ="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />     </ parsers >     < context >       < resource uri ="config://spring/objects" />
     
<!-- Dao -->       < resource uri ="assembly://Customer.Dao/Customer.Dao.Config/Dao.xml" />       <!-- Service -->       < resource uri ="assembly://Customer.Service/Customer.Service.Config/Service.xml" />
   
</ context >     < objects xmlns ="http://www.springframework.net"              xmlns:aop ="http://www.springframework.net/aop" >
     
< object id ="Customer.Host" type ="Customer.Host.Implement.CustomerServer, Customer.Host" >         < property name ="Manager" ref ="Customer.CustomerManager" />       </ object >
   
</ objects >   </ spring >
 
< appSettings >     < add key ="Spring.Data.NHibernate.Support.SessionScope.SessionFactoryObjectName" value ="NHibernateSessionFactory" />   </ appSettings >
 
< system.web >     < compilation debug ="true" targetFramework ="4.0" />
   
< httpModules >       < add name ="Spring" type ="Spring.Context.Support.WebSupportModule, Spring.Web" />     </ httpModules >
 
</ system.web >   < system.serviceModel >     < services >       < service name ="Customer.Host" >         < endpoint address ="" binding ="wsHttpBinding" bindingConfiguration ="ServerBinding" contract ="Customer.Host.ICustomerContract" />         < endpoint address ="mex" binding ="mexHttpBinding" contract ="IMetadataExchange" />       </ service >     </ services >     < bindings >       < wsHttpBinding >         < binding name ="ServerBinding" transactionFlow ="true" >         </ binding >       </ wsHttpBinding >     </ bindings >     < behaviors >       < serviceBehaviors >         < behavior >           <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->           < serviceMetadata httpGetEnabled ="true" />           <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->           < serviceDebug includeExceptionDetailInFaults ="true" />         </ behavior >       </ serviceBehaviors >     </ behaviors >     < serviceHostingEnvironment multipleSiteBindingsEnabled ="true" aspNetCompatibilityEnabled ="true" />   </ system.serviceModel > < system.webServer >     < modules runAllManagedModulesForAllRequests ="true" />   </ system.webServer >   </ configuration >

 

 

 

<% @ ServiceHost Language = " C# " Debug = " true " Service = " Customer.Host " Factory = " Spring.ServiceModel.Activation.ServiceHostFactory " %>

 

 

  ②.Order

  

Web.config
<? xml version="1.0" encoding="utf-8" ?> < configuration >
  ..........
 
<!-- spring配置 -->   < spring xmlns ="http://www.springframework.net" >     < parsers >       < parser type ="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />       < parser type ="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />     </ parsers >     < context >       < resource uri ="config://spring/objects" />
     
<!-- Dao -->       < resource uri ="assembly://Order.Dao/Order.Dao.Config/Dao.xml" />       <!-- Service -->       < resource uri ="assembly://Order.Service/Order.Service.Config/Service.xml" />
   
</ context >     < objects xmlns ="http://www.springframework.net"              xmlns:aop ="http://www.springframework.net/aop" >
     
< object id ="Order.Host" type ="Order.Host.Implement.OrderServer, Order.Host" >         < property name ="Manager" ref ="Order.OrderManager" />       </ object >
   
</ objects >   </ spring >
 
< appSettings >     < add key ="Spring.Data.NHibernate.Support.SessionScope.SessionFactoryObjectName" value ="NHibernateSessionFactory" />   </ appSettings >
 
< system.web >     < compilation debug ="true" targetFramework ="4.0" />
   
< httpModules >       < add name ="Spring" type ="Spring.Context.Support.WebSupportModule, Spring.Web" />     </ httpModules >
 
</ system.web >   < system.serviceModel >     < services >       < service name ="Order.Host" >         < endpoint address ="" binding ="wsHttpBinding" bindingConfiguration ="ServerBinding" contract ="Order.Host.IOrderContract" />         < endpoint address ="mex" binding ="mexHttpBinding" contract ="IMetadataExchange" />       </ service >     </ services >     < bindings >       < wsHttpBinding >         < binding name ="ServerBinding" transactionFlow ="true"   >         </ binding >       </ wsHttpBinding >     </ bindings >     < behaviors >       < serviceBehaviors >         < behavior >           <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->           < serviceMetadata httpGetEnabled ="true" />           <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->           < serviceDebug includeExceptionDetailInFaults ="true" />         </ behavior >       </ serviceBehaviors >     </ behaviors >     < serviceHostingEnvironment multipleSiteBindingsEnabled ="true" aspNetCompatibilityEnabled ="true" />   </ system.serviceModel > < system.webServer >     < modules runAllManagedModulesForAllRequests ="true" />   </ system.webServer >   </ configuration >

 

 

 

<% @ ServiceHost Language = " C# " Debug = " true " Service = " Order.Host " Factory = " Spring.ServiceModel.Activation.ServiceHostFactory " %>

 

 

  四、客户端

 

HostTest
[TestFixture]     public class HostTest     {         private CustomerContractClient customerProxy;
       
private OrderContractClient orderProxy;
        [SetUp]        
public void Init()         {             customerProxy = new CustomerContractClient();             orderProxy = new OrderContractClient();         }
        [Test]        
public void InitData()         {             using (TransactionScope scope = new TransactionScope())             {                 customerProxy.Save( new CustomerInfo                 {                     Name = " 刘冬 "                 });
                scope.Complete();             }         }
        [Test]        
public void DistributedTransactionTest()         {             using (TransactionScope scope = new TransactionScope())             {                 try                 {                     CustomerInfo customer = customerProxy.Get( 1 );                     orderProxy.Save( new OrderInfo                     {                         Address = " 中国北京 " ,                         CustomerId = ( int )customer.ID,                         OrderDate = DateTime.Now                     });                     customer.Money += 1000 ;                     customerProxy.Update(customer);                     scope.Complete();                     Console.WriteLine( " 分布式事务已提交 " );                 }                 catch (Exception ex)                 {                     Transaction.Current.Rollback();                     Console.WriteLine( " 发送错误:分布式事务已回滚 " );                 }             }         }     }

 

 

 

  

 

  五、运行效果

  1.初始化数据

  

 

  2.建立第一张订单,订金小于3000

 

  

 

  

  3.建立第一张订单,订金小于3000

 

 

  4.建立第一张订单,订金等于3000

 

 

  5.建立第一张订单,订金大于3000,事务回滚。

 

 

 

  好了,基于Web Service的分布式事务已经实现了。

 

  代码下载

  出处:http://www.cnblogs.com/GoodHelper/archive/2010/07/30/SpringNetDistributedTransaction2.html

  欢迎转载,但需保留版权。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值