2011-01-07 27 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