使用TransactionScope的NHibernate

34

谁能给我一个使用TransactionScope与NHibernate的快速概述?我需要在会话/IEnlistmentNotification等方面做任何特殊处理才能使其工作吗?有什么陷阱我应该担心的吗?例如,我可以替换所有Hibernate事务吗:

var transaction = session.BeginTransaction();
try
{
    // code
    transaction.Commit();
}
catch (Exception)
{
    transaction.Rollback();
}

用这个怎么样?

using (var scope = new TransactionScope())
{
    // code
    scope.Complete();
}
6个回答

19

我已经使用nHibernate 2.1一段时间了,在遇到一些问题并尝试了很多变化后,我们采用了以下方法,如避免NHibernate和TransactionScope泄漏连接中所述:

        using (var scope = new TransactionScope(TransactionScopeOption.Required))
        {
            using (var session = sessionFactory.OpenSession())
            using (var transaction = session.BeginTransaction())
            {
                // do what you need to do with the session
                transaction.Commit();
            }
            scope.Complete();
        }

因为我们正在使用MSMQ和WCF,所以必须使用环境事务。

我们发现不使用session.BeginTransaction()会导致连接泄漏。 我们还发现,在提交事务后重新使用会话会导致竞态条件(nHibernate不是线程安全的,DTSC提交/回滚会在后台线程上发生)。


你确定吗?你测试过了吗?从记忆中来看,内部事务与父级作用域不是“独立的”。直到 scope.Complete() 执行,事务才真正提交。 - Iain
嘿,谢谢回复,我知道这篇文章很旧了。我期望它会像你说的那样运作,但事实似乎并非如此。我们已经进行了测试,请看我在这里做的测试:https://gist.github.com/2759471(抱歉,没有时间简化sessionFactory的东西,所以有点冗长)。 - undefined
一眼看上去,为什么第165行断言为0,而第162行已经保存了1。第162行的存在是为什么? - Iain
我刚刚阅读了这篇文章,并且也许问题在于CE不支持嵌套事务或分布式事务。 - Iain
1
我不再处理那段代码了,而且我的记忆已经模糊了。你的错误看起来很熟悉。我记得的是因为事务在后台线程上提交,所以当它提交时,主线程已经重新打开了会话。然后后台线程清理会话,我想它用类似于你的错误或无效的事务状态错误的方式终止了主线程。这个问题可能与此相关:http://stackoverflow.com/questions/8846210/nhibernate-failing-on-deferred-load-action。 - Iain
显示剩余7条评论

7

我已经使用不同的供应商进行测试,它可以正常工作。如果没有 "scope.Complete()",事务将会回滚。如果涉及到多个持久资源,则可能需要在机器上运行 MSDTC。在这种情况下,MSDTC 将自动检测环境中的 ADO.NET 事务并管理整个过程。


3
默认情况下,TransactionScope使用轻量级事务管理器来监视升级。根据MSDN:只要涉及不超过一个持久性资源管理器,让底层资源(如Microsoft SQL Server 2005)管理事务就是没有问题的。在这种情况下,LTM实际上不需要管理事务 - 它的作用应该减少到监视是否需要升级事务。 - Henrik
1
此外,如果您使用的是 SqlConnection(假设这是您的数据库),则 1)它们在后台进行池化,并且 2)您可以从单个线程打开多个连接,而不会升级事务,只要它们不共享事务。升级事务意味着开始使用 MSDTC。 - Henrik

6
我相信只要您遵守一些限制条件,就可以用这个替换NHibernate事务,正如一些人已经说过的那样:
  • 使用相对较新的NHibernate版本(>=3.1)。
  • 底层的ADO.NET数据提供程序必须支持TransactionScope(SQL-Server、Oracle >= 10与ODP.NET都可以)。
  • 在TransactionScope内创建NH会话以确保它被注册。
  • 手动处理NHibernate 刷新。另请参见此处此处
  • 准备分布式事务。 如果您在一个作用域内创建多个会话,则最初的本地事务可能会升级为分布式事务。我甚至看到这种情况发生在使用ODAC 11.2.0.3.20的Oracle 11.2 DB上,即使连接字符串相同。在SQL-Server 2008 R2上,它没有升级。(顺便说一下,可以通过观察Transaction.Current.TransactionInformation.DistributedIdentifier来看到本地事务的DistributedIdentifier为null。)虽然分布式事务具有一些优点,但它们更昂贵,而且设置它们需要额外的麻烦。(请参见此处如何为Oracle执行此操作)。为了确保在Oracle中永远不会升级,请在您的连接字符串中设置“可升级事务=local”。如果某些代码尝试这样做,它会创建一个异常。
希望就这些了;-)
PS:我加上了Oracle的详细信息,因为它们是我的关注焦点,其他人可能也会受益。

6

以上方法在使用支持轻量级事务管理器的连接提供程序(如SQL Server 2005/2008)时可以正常工作。

如果您使用的是SQL Server 7/2000,则即使只涉及一个数据库/资源,所有事务也将成为分布式事务。这在大多数情况下可能不是您想要的,并且会对性能产生昂贵的影响。

因此,请检查您的连接提供程序和数据库服务器组合是否适用于使用TransactionScope。


1
此外,如果您正在使用TransactionScope,请升级到NHibernate 2.1。只有在2.1中,NH与TransactionScope才真正获得了良好的集成。

1
@Rohit - 你能提供更多关于你的答案的细节吗? - Richard Ev

1
根据Fabio Maulo在与NH-2107相关的评论中所说:

您可以使用TransactionScope,同时应继续使用NH的事务。 您从哪里读到使用TransactionScope意味着避免使用NH的事务?

我本来以为不需要显式使用NHibernate的事务,但显然这是最佳实践。


我仍在寻找该问题的权威答案。在 NH 3.0 中,似乎不需要显式使用 - 除非是为了使自动刷新正常工作。 - Jeff Sternal
它还会导致SQL Compact出现问题(尝试创建嵌套事务,这对我来说似乎是一个错误):http://stackoverflow.com/questions/8127735/how-can-i-use-transactionscope-with-sql-compact-4-0-and-nhibernate - Travis
我在这里更新了Andrew的链接,讨论发生在以下网址:https://nhibernate.jira.com/browse/NH-2107 - C3PO
请注意,截至2019年4月4日,此内容可能已不再适用。当前文档中写道:“可以使用TransactionScope代替NHibernate ITransaction。请勿同时使用两者。” 参考:https://nhibernate.info/doc/nhibernate-reference/transactions.html#transactions-scopes - Remi Despres-Smyth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接