2011-01-07 48 views
6

我一直在使用Spring.NET和NHibernate一些年,我非常滿意。不過,我一直在玩多線程,Reactive Extensions和最終的Task Parallel Library,這是一個很棒的框架。不幸的是,由於NHiberntate的會話不是線程安全的,所以所有類型的多線程方法都失敗了。C#任務並行庫和NHibernate/Spring.NET

我在問你,我如何從並行編程中受益並仍在使用NHibernate。

例如:我有一個CustomerRegistrationService類方法Register執行多個任務:

ICustumer customer = this.CreateCustomerAndAdresses(params); 
this.CreateMembership(customer); 
this.CreateGeoLookups(customer.Address); 
this.SendWelcomeMail(customer); 

最後兩個方法將是理想的人選運行並行,CreateGeoLookups調用一些Web服務,以確定客戶的地緣位置解決並創建一些新的實體,並更新客戶本身。 SendWelcomMail做它說的。

由於CreateGeoLookups確實使用了NHibernate(儘管通過版本庫對象使得NHibernate通過Interfaces/Dependency Inection非常隱藏),但它不能與Task.Factory.StarNew(...)或其他線程機制一起使用。

我的問題不是解決我所描述的這個問題,但我想聽取您關於NHibenrate,Spring.NET和並行方法的信息。

非常感謝您 最大

回答

8

在NH它不是線程安全的,但ISessionFactory完全是線程安全的,輕鬆支持什麼,似乎你以後的Isession。如果您已經設計了會話生命週期管理(以及依賴於它的存儲庫),以便跨越呼叫假設一個單一的一致性ISession,那麼,是的,您將遇到這種麻煩。但是,如果你已經設計了會話處理模式來假設只有一個ISessionFactory,但不能對ISession做出假設,那麼就沒有任何內在的阻止你與NH並行交互。

儘管您沒有具體提及您的用例,但需要注意的是,在以網絡爲中心的用例中(例如,對於Spring.NET用戶來說什麼是非常常見的情況,以及許多其他NH管理框架),ISession管理中經常使用的'Session-Per-Request'模式(在Spring.NET中經常被稱爲'Open Session In View'或'OSIV')將不起作用,而你將需要切換到ISession生命週期的不同持續時間。這是因爲(正如其名稱所示)session-per-request/OSIV模式使得(現在不正確)假定在每個HttpRequest的持續時間內只有一個ISession實例(並且可能你希望成爲產生這些並行的NH調用都在Web用例的單個HttpRequest的上下文中)。

顯然,在很少有與session-per-request類似的概念的非網絡案例中,由於會話生命週期管理很少像細化/短命一樣,因此您不可能遇到此問題就像在基於網絡的應用程序中一樣。

希望這會有所幫助。

-Steve B.

0

好的,謝謝你的回答。我知道'ISession不是線程安全的,但ISessionFactory完全是線程安全的'。例如,在上面的代碼中,我的問題是整個操作被封裝在一個事務中。因此,主線程#1上的this.CreateCustomerAndAdresses(params)將用於實例ISession#1和事務#1。並行調用其他三個將創建三個線程和三個更多的會話和事務,導致數據庫超時在我的情況。我的假設是事務#1沒有成功提交,因爲它等待三個併發任務完成。但是,當事務處於活動狀態時,三個併發任務嘗試從數據庫中讀取,導致死鎖/超時。

那麼有什麼方法可以告訴其他線程/會話不要創建一個新的事務,但使用主事務#1?

我使用Spring.NET中的TxScopeTransactionManager,它使用DTC(System.Transactions)。我已經搜索了一下,也許System.Transactions.DependentTransaction可以工作,但沒有線索如何將其整合到我的Spring.NET事務管理方案中。

謝謝

+0

你真的應該編輯你的主要問題與這個問題。這些中的任何一個都可以作爲答案嗎? – paqogomez 2013-09-05 03:21:59

2

這是一個很難的事情,你要求。 DTC必須小心謹慎。

我可能知道的唯一解決方案是使用可靠的事務消息(例如MSMQ + NServiceBus/MassTransit)。

這樣的設計可以讓你做到這一點。它應該是這樣的:

var customerUid=CreateCustomers(); 
Bus.Publish(new CustomerCreatedEvent() { CustomerUid = customerUid}); 

那麼你可以使用兩個事件處理(電抗器),其處理該事件,併發送電子郵件或創建查找。

這不會允許您共享交易,但會確保當客戶創建成功時反應器運行(在新交易中)。 另外這與TPL無關。

+0

對於所述問題,我確實認爲這是更明智的做法。您不希望創建客戶,因爲您無法創建地理數據或發送電子郵件,而是希望在這些過程完成之前重試這兩個過程。這非常適合消息隊列體系結構。 – 2013-05-16 02:19:10