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
歡迎轉載,但需保留版權。
posted on 2013-02-23 14:49?Alfa 閱讀(...) 評論(...) 編輯 收藏轉載于:https://www.cnblogs.com/wuyifu/archive/2013/02/23/2923436.html
總結
以上是生活随笔為你收集整理的spring.net nhibernate 分布布式事务(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: objdump and readelf
- 下一篇: 了解其他默认参数