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

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

原文地址:https://www.cnblogs.com/wuyifu/p/2923436.html