zoukankan      html  css  js  c++  java
  • spring.net nhibernate 分布布式事务(下)

    摘自: 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
        publicinterface ICustomerManager     {         CustomerInfo Get(object id);
           
    object Save(CustomerInfo entity);
           
    void Update(CustomerInfo entity);     }
       
    publicclass CustomerManager : ICustomerManager     {         private ICustomerDao Dao { get; set; }
           
    public CustomerInfo Get(object id)         {             return Dao.Get(id);         }
           
    publicobject Save(CustomerInfo entity)         {             return Dao.Save(entity);         }
           
    publicvoid Update(CustomerInfo entity)         {             if (entity.Money >3000)             {                 thrownew 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
        publicinterface IOrderManager     {         object Save(OrderInfo entity);     }
       
    publicclass OrderManager : IOrderManager     {         public IOrderDao Dao { get; set; }
           
    publicobject 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)]     publicinterface 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)]     publicclass CustomerServer : ICustomerContract     {         public ICustomerManager Manager { get; set; }
            [OperationBehavior(TransactionScopeRequired
    =true)]         public CustomerInfo Get(object id)         {             return Manager.Get(id);         }
            [OperationBehavior(TransactionScopeRequired
    =true)]         publicobject Save(CustomerInfo entity)         {
               
    return Manager.Save(entity);         }
            [OperationBehavior(TransactionScopeRequired
    =true)]         publicvoid Update(CustomerInfo entity)         {             Manager.Update(entity);         }

      ②.Order

      

    IOrderContract
        [ServiceContract(SessionMode = SessionMode.Required)]     publicinterface IOrderContract     {         [OperationContract]         [TransactionFlow(TransactionFlowOption.Allowed)]         object Save(OrderInfo entity);     }
       [AspNetCompatibilityRequirements(RequirementsMode
    = AspNetCompatibilityRequirementsMode.Required)]     publicclass OrderServer : IOrderContract     {         public IOrderManager Manager { get; set; }
            [OperationBehavior(TransactionScopeRequired
    =true)]         publicobject 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
        publicclass Global : System.Web.HttpApplication     {
           
    protectedvoid Application_Start(object sender, EventArgs e)         {             log4net.Config.XmlConfigurator.Configure();         }
           
    protectedvoid Session_Start(object sender, EventArgs e)         {             SessionScope sessionScope =new SessionScope("appSettings", typeof(SessionScope), false);             sessionScope.Open();             HttpContext.Current.Session["SessionScope"] = sessionScope;         }
                   
    protectedvoid 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]     publicclass HostTest     {         private CustomerContractClient customerProxy;
           
    private OrderContractClient orderProxy;
            [SetUp]        
    publicvoid Init()         {             customerProxy =new CustomerContractClient();             orderProxy =new OrderContractClient();         }
            [Test]        
    publicvoid InitData()         {             using (TransactionScope scope =new TransactionScope())             {                 customerProxy.Save(new CustomerInfo                 {                     Name ="刘冬"                 });
                    scope.Complete();             }         }
            [Test]        
    publicvoid 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

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

  • 相关阅读:
    LeetCode
    LeetCode
    136. Single Number
    485. Max Consecutive Ones
    THinkPHP的认识
    MySql的简单数据类型区别与认识
    关于数据库中字符串类型的区别与使用方法
    关于数据库的建立以及表格的建立
    关于xampp集成开发环境的建立与初步认识
    如何关闭SQL进程
  • 原文地址:https://www.cnblogs.com/wuyifu/p/2923436.html
Copyright © 2011-2022 走看看