我如何在NHibernate中进行嵌套事务?

时间:2009-07-28 05:43:03

标签: sql-server nhibernate session transactions nested

我可以在NHibernate中执行嵌套事务,我该如何实现它们?我正在使用SQL Server 2008,所以支持肯定在DBMS中。

我发现如果我尝试这样的话:

using (var outerTX = UnitOfWork.Current.BeginTransaction())
{
    using (var nestedTX = UnitOfWork.Current.BeginTransaction())
    {
        ... do stuff
        nestedTX.Commit();
    }

    outerTX.Commit();
}

然后到outerTX.Commit()时,事务变为非活动状态,并在会话AdoTransaction上产生ObjectDisposedException。

因此我们应该创建嵌套的NHibernate会话吗?或者是否有其他类我们应该用它来包装事务(我听说过TransactionScope,但我不确定那是什么)?

我现在正在使用Ayende's UnitOfWork implementation(感谢Sneal)。

原谅这个问题的任何天真,我还是NHibernate的新手。

谢谢!

编辑:我发现您可以使用TransactionScope,例如:

using (var transactionScope = new TransactionScope())
{
    using (var tx = UnitOfWork.Current.BeginTransaction())
    {
        ... do stuff
        tx.Commit();
    }

    using (var tx = UnitOfWork.Current.BeginTransaction())
    {
        ... do stuff
        tx.Commit();
    }

    transactionScope.Commit();
}

然而,我对此并不感到兴奋,因为它锁定了我们使用SQL Server,而且我发现如果数据库是远程的,那么你必须担心启用MSDTC ......还有一个组件出错了。嵌套事务在SQL中是如此有用和容易,我认为NHibernate会有一些模仿相同的方法...

4 个答案:

答案 0 :(得分:13)

NHibernate会话不支持嵌套事务。

以下测试在版本2.1.2中始终为真:

var session = sessionFactory.Open();
var tx1 = session.BeginTransaction();
var tx2  = session.BeginTransaction();
Assert.AreEqual(tx1, tx2);

您需要将其包装在TransactionScope中以支持嵌套事务。

必须启用MSDTC,否则您将收到错误:

{"Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool."}

答案 1 :(得分:3)

正如Satish所说,NHibernate不支持嵌套事务。我没有遇到需要嵌套事务的情况,但当然我遇到了一些问题,如果其他人已经在其他工作单元中活动,我就不得不忽略创建事务。

下面的博客链接提供了NHibernate的示例实现,但也适用于SQL Server: http://rajputyh.blogspot.com/2011/02/nested-transaction-handling-with.html

答案 2 :(得分:2)

我一直在努力解决这个问题。我还有另一个问题。

我想在各个服务容器中实现事务 - 因为这使得它们自包含 - 但是然后能够在更大的事务中嵌套一堆这些服务方法并在必要时回滚整个服务。

因为我正在使用Rhino Commons我现在要尝试使用With.Transaction方法进行重构。基本上它允许我们编写代码,好像事务是嵌套的,但实际上只有一个。

例如:

private Project CreateProject(string name)
{
    var project = new Project(name);
    With.Transaction(delegate
    {
        UnitOfWork.CurrentSession.Save(project);
    });
    return project;
}

private Sample CreateSample(Project project, string code)
{
    var sample = new Sample(project, code);
    With.Transaction(delegate
    {
        UnitOfWork.CurrentSession.Save(sample);
    });
    return sample;
}

private void Test_NoNestedTransaction()
{
  var project = CreateProject("Project 1");
}

private void TestNestedTransaction()
{
  using (var tx = UnitOfWork.Current.BeginTransaction())
  {
      try
      {
          var project = CreateProject("Project 6");
          var sample = CreateSample(project, "SAMPLE006", true);
      }
      catch
      {
          tx.Rollback();
          throw;
      }
      tx.Commit();
  }
}

Test_NoNestedTransaction()中,我们单独创建一个项目,没有更大事务的上下文。在这种情况下,在CreateSample中,将创建并提交新事务,或者在发生异常时回滚。

Test_NestedTransaction()中,我们创建了一个示例和一个项目。如果出现任何问题,我们希望两者都回滚。实际上,CreateSampleCreateProject中的代码将运行,就好像根本没有交易一样;它完全是外部事务决定是回滚还是提交,并根据是否抛出异常来执行。真的,这就是我为外部交易使用手动创建的交易的原因;所以我可以控制是提交还是回滚,而不是默认为 on-exception-rollback-else-commit

你可以在没有Rhino.Commons的情况下通过在代码中添加大量此类内容来实现相同的目标:

if (!UnitOfWork.Current.IsInActiveTransaction)
{
  tx = UnitOfWork.Current.BeginTransaction();
}

_auditRepository.SaveNew(auditEvent);
if (tx != null)
{
  tx.Commit();
}

......等等。但With.Transaction,尽管需要创建匿名代表的笨拙,但这样做非常方便。

这种方法优于使用TransactionScope(除了依赖于MSDTC)的一个优点是,无论有多少方法,在最终的外部事务提交中应该只对数据库进行一次刷新。被称为中间。换句话说,我们不需要像往常一样将未提交的数据写入数据库,我们总是将它写入本地NHibernate缓存。

简而言之,此解决方案无法对您的交易提供最终控制权,因为它不会使用多个交易。我想我可以接受,因为嵌套事务绝不是每个DBMS都普遍支持的。但现在也许我至少可以编写代码而不必担心我们是否已经在交易中。

答案 3 :(得分:1)

如果要使用Ayende's UnitOfWork implementation嵌套,那么该实现不支持嵌套。您正在使用的实现(至少对于Web应用程序)的另一个问题是它在静态变量中保留在ISession实例上。

由于这些原因,我昨天刚刚重写了我们的UnitOfWork,它最初是基于加布里埃尔的。

我们不使用UnitOfWork.Current.BeginTransaction(),我们使用UnitofWork.TransactionalFlush(),它在最后创建一个单独的事务来立即刷新所有更改。

using (var uow = UnitOfWork.Start())
{
     var entity = repository.Get(1);
     entity.Name = "Sneal";
     uow.TransactionalFlush();
}