zoukankan      html  css  js  c++  java
  • 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

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


    原文链接:http://www.cnblogs.com/GoodHelper/archive/2010/07/30/SpringNetDistributedTransaction2.html

  • 相关阅读:
    Python中return self的用法
    多分类问题的交叉熵计算
    Python爬虫之足球小将动漫(图片)下载
    Sklearn中二分类问题的交叉熵计算
    TensorFlow.js入门(一)一维向量的学习
    MySql 流程控制经典案列讲解
    MySql 流程控制
    MySql 函数
    MySql 存储过程
    MySql 视图
  • 原文地址:https://www.cnblogs.com/chenzhao/p/2581440.html
Copyright © 2011-2022 走看看