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 { getset; }

            
    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 { getset; }

            
    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 { getset; }

            [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 { getset; }

            [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

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

  • 相关阅读:
    图片验证码, 登录, 注销, 修改密码
    注册页面及注册功能实现
    高级配置文件, csrf, django settings源码, django auth模块, 文件配置的插拔式设计
    cookie操作, session操作, django中间件
    半自动创建多对多关系表, forms组件
    sweetalert, bulk_create, 分页器
    orm查询优化, MVC与MTV, choices参数, ajax
    聚合查询, 分组查询, F与Q查询, 常见字段及参数, 自定义Char字段, 事务操作
    Contest2058
    ACM版《孔乙己》
  • 原文地址:https://www.cnblogs.com/GoodHelper/p/SpringNetDistributedTransaction2.html
Copyright © 2011-2022 走看看