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

时间:2022-08-26 00:11:02

Can I do nested transactions in NHibernate, and how do I implement them? I'm using SQL Server 2008, so support is definitely in the DBMS.

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

I find that if I try something like this:

我发现如果我尝试这样的事情:

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

    outerTX.Commit();
}

then by the time it comes to outerTX.Commit() the transaction has become inactive, and results in a ObjectDisposedException on the session AdoTransaction.

然后,当它涉及到outerTX.Commit()时,事务已经变得不活跃,并导致会话AdoTransaction上的objectdispose sedexception。

Are we therefore supposed to create nested NHibernate sessions instead? Or is there some other class we should use to wrap around the transactions (I've heard of TransactionScope, but I'm not sure what that is)?

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

I'm now using Ayende's UnitOfWork implementation (thanks Sneal).

我现在正在使用Ayende的UnitOfWork实现(谢谢Sneal)。

Forgive any naivety in this question, I'm still new to NHibernate.

请原谅我在这个问题上的天真,我对NHibernate还不熟悉。

Thanks!

谢谢!

EDIT: I've discovered that you can use TransactionScope, such as:

编辑:我发现您可以使用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();
}

However I'm not all that excited about this, as it locks us in to using SQL Server, and also I've found that if the database is remote then you have to worry about having MSDTC enabled... one more component to go wrong. Nested transactions are so useful and easy to do in SQL that I kind of assumed NHibernate would have some way of emulating the same...

然而,我对此并不感到兴奋,因为它将我们锁在使用SQL Server中,而且我还发现,如果数据库是远程的,那么您必须担心启用MSDTC……还有一个部件出错。嵌套事务在SQL中是非常有用和容易的,我假设NHibernate会有一些类似的方法。

4 个解决方案

#1


13  

NHibernate sessions don't support nested transactions.

NHibernate会话不支持嵌套事务。

The following test is always true in version 2.1.2:

下面的测试在版本2.1.2中始终是正确的:

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

You need to wrap it in a TransactionScope to support nested transactions.

您需要将它封装在TransactionScope中,以支持嵌套的事务。

MSDTC must be enabled or you will get error:

必须启用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."}

{“分布式事务管理器(MSDTC)的网络访问已被禁用。请使用组件服务管理工具在MSDTC的安全配置中启用DTC进行网络访问。

#2


3  

As Satish suggested, nested transactions are not supported in NHibernate. I've not come across scenarios where nested transactions were needed, but certainly I've faced problems where I had to ignore creating transactions if other ones were already active in other units of work.

正如Satish所建议的,NHibernate不支持嵌套事务。我没有遇到需要嵌套事务的场景,但是我肯定遇到过这样的问题:如果其他事务已经在其他工作单元中活动,我就必须忽略创建事务。

The blog link below provides an example implementation for NHibernate, but should also work for SQL server: http://rajputyh.blogspot.com/2011/02/nested-transaction-handling-with.html

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

#3


2  

I've been struggling with this for a while now. Am going to have another crack at it.

我已经挣扎了一段时间了。我要再试一次。

I want to implement transactions in individual service containers - because that makes them self-contained - but then be able to nest a bunch of those service methods within a larger transaction and rollback the whole lot if necessary.

我想在单独的服务容器中实现事务—因为这使它们成为自包含的—但是可以在较大的事务中嵌套这些服务方法,并在必要时回滚整个服务容器。

Because I'm using Rhino Commons I'm now going to try refactoring using the With.Transaction method. Basically it allows us to write code as if transactions were nested, though in reality there is only one.

因为我正在使用Rhino公有物,所以我现在要尝试使用With进行重构。事务的方法。基本上,它允许我们像嵌套事务一样编写代码,尽管实际上只有一个。

For example:

例如:

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();
  }
}

In Test_NoNestedTransaction(), we are creating a project alone, without the context of a larger transaction. In this case, in CreateSample a new transaction will be created and committed, or rolled back if an exception occurs.

在Test_NoNestedTransaction()中,我们只创建一个项目,而不考虑更大事务的上下文。在这种情况下,在CreateSample中,一个新的事务将被创建和提交,或者在出现异常时回滚。

In Test_NestedTransaction(), we are creating both a sample and a project. If anything goes wrong, we want both to be rolled back. In reality, the code in CreateSample and CreateProject will run just as if there were no transactions at all; it is entirely the outer transaction that decides whether to rollback or commit, and does so based on whether an exception is thrown. Really that's why I'm using a manually created transaction for the outer transaction; so we I have control over whether to commit or rollback, rather than just defaulting to on-exception-rollback-else-commit.

在Test_NestedTransaction()中,我们同时创建一个示例和一个项目。如果出现任何问题,我们都希望被回滚。实际上,CreateSample和CreateProject中的代码将会运行,就像根本没有事务一样;它完全是由外部事务决定是回滚还是提交,并根据是否抛出异常来执行。这就是为什么我用手工创建的事务处理外部事务;因此,我们可以控制是否提交或回滚,而不只是默认为on-exception-rollback- commit。

You could achieve the same thing without Rhino.Commons by putting a whole lot of this sort of thing through your code:

没有Rhino,你也可以实现同样的目的。通过在你的代码中加入很多这样的东西

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

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

... and so on. But With.Transaction, despite the clunkiness of needing to create anonymous delegates, does that quite conveniently.

…等等。但随着。尽管创建匿名委托非常麻烦,但事务可以很方便地完成这一任务。

An advantage of this approach over using TransactionScopes (apart from the reliance on MSDTC) is that there ought to be just a single flush to the database in the final outer-transaction commit, regardless of how many methods have been called in-between. In other words, we don't need to write uncommitted data to the database as we go, we're always just writing it to the local NHibernate cache.

与使用transactionscope(除了对MSDTC的依赖)相比,这种方法的一个优点是,在最终的外部事务提交中,无论调用多少方法,都应该只有一个对数据库的刷新。换句话说,我们不需要将未提交的数据写入数据库,我们只需要将其写入本地的NHibernate缓存。

In short, this solution doesn't offer ultimate control over your transactions, because it doesn't ever use more than one transaction. I guess I can accept that, since nested transactions are by no means universally supported in every DBMS anyway. But now perhaps I can at least write code without worrying about whether we're already in a transaction or not.

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

#4


1  

That implementation doesn't support nesting, if you want nesting use Ayende's UnitOfWork implementation. Another problem with the implementation your are using (at least for web apps) is that it holds onto the ISession instance in a static variable.

这个实现不支持嵌套,如果您想要嵌套使用Ayende的UnitOfWork实现。您正在使用的实现(至少对于web应用程序)的另一个问题是,它将ISession实例放在一个静态变量中。

I just rewrote our UnitOfWork yesterday for these reasons, it was originally based off of Gabriel's.

由于这些原因,我昨天才重新写了我们的单位,它最初是基于盖伯瑞尔的。

We don't use UnitOfWork.Current.BeginTransaction(), we use UnitofWork.TransactionalFlush(), which creates a separate transaction at the very end to flush all the changes at once.

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

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

#1


13  

NHibernate sessions don't support nested transactions.

NHibernate会话不支持嵌套事务。

The following test is always true in version 2.1.2:

下面的测试在版本2.1.2中始终是正确的:

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

You need to wrap it in a TransactionScope to support nested transactions.

您需要将它封装在TransactionScope中,以支持嵌套的事务。

MSDTC must be enabled or you will get error:

必须启用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."}

{“分布式事务管理器(MSDTC)的网络访问已被禁用。请使用组件服务管理工具在MSDTC的安全配置中启用DTC进行网络访问。

#2


3  

As Satish suggested, nested transactions are not supported in NHibernate. I've not come across scenarios where nested transactions were needed, but certainly I've faced problems where I had to ignore creating transactions if other ones were already active in other units of work.

正如Satish所建议的,NHibernate不支持嵌套事务。我没有遇到需要嵌套事务的场景,但是我肯定遇到过这样的问题:如果其他事务已经在其他工作单元中活动,我就必须忽略创建事务。

The blog link below provides an example implementation for NHibernate, but should also work for SQL server: http://rajputyh.blogspot.com/2011/02/nested-transaction-handling-with.html

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

#3


2  

I've been struggling with this for a while now. Am going to have another crack at it.

我已经挣扎了一段时间了。我要再试一次。

I want to implement transactions in individual service containers - because that makes them self-contained - but then be able to nest a bunch of those service methods within a larger transaction and rollback the whole lot if necessary.

我想在单独的服务容器中实现事务—因为这使它们成为自包含的—但是可以在较大的事务中嵌套这些服务方法,并在必要时回滚整个服务容器。

Because I'm using Rhino Commons I'm now going to try refactoring using the With.Transaction method. Basically it allows us to write code as if transactions were nested, though in reality there is only one.

因为我正在使用Rhino公有物,所以我现在要尝试使用With进行重构。事务的方法。基本上,它允许我们像嵌套事务一样编写代码,尽管实际上只有一个。

For example:

例如:

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();
  }
}

In Test_NoNestedTransaction(), we are creating a project alone, without the context of a larger transaction. In this case, in CreateSample a new transaction will be created and committed, or rolled back if an exception occurs.

在Test_NoNestedTransaction()中,我们只创建一个项目,而不考虑更大事务的上下文。在这种情况下,在CreateSample中,一个新的事务将被创建和提交,或者在出现异常时回滚。

In Test_NestedTransaction(), we are creating both a sample and a project. If anything goes wrong, we want both to be rolled back. In reality, the code in CreateSample and CreateProject will run just as if there were no transactions at all; it is entirely the outer transaction that decides whether to rollback or commit, and does so based on whether an exception is thrown. Really that's why I'm using a manually created transaction for the outer transaction; so we I have control over whether to commit or rollback, rather than just defaulting to on-exception-rollback-else-commit.

在Test_NestedTransaction()中,我们同时创建一个示例和一个项目。如果出现任何问题,我们都希望被回滚。实际上,CreateSample和CreateProject中的代码将会运行,就像根本没有事务一样;它完全是由外部事务决定是回滚还是提交,并根据是否抛出异常来执行。这就是为什么我用手工创建的事务处理外部事务;因此,我们可以控制是否提交或回滚,而不只是默认为on-exception-rollback- commit。

You could achieve the same thing without Rhino.Commons by putting a whole lot of this sort of thing through your code:

没有Rhino,你也可以实现同样的目的。通过在你的代码中加入很多这样的东西

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

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

... and so on. But With.Transaction, despite the clunkiness of needing to create anonymous delegates, does that quite conveniently.

…等等。但随着。尽管创建匿名委托非常麻烦,但事务可以很方便地完成这一任务。

An advantage of this approach over using TransactionScopes (apart from the reliance on MSDTC) is that there ought to be just a single flush to the database in the final outer-transaction commit, regardless of how many methods have been called in-between. In other words, we don't need to write uncommitted data to the database as we go, we're always just writing it to the local NHibernate cache.

与使用transactionscope(除了对MSDTC的依赖)相比,这种方法的一个优点是,在最终的外部事务提交中,无论调用多少方法,都应该只有一个对数据库的刷新。换句话说,我们不需要将未提交的数据写入数据库,我们只需要将其写入本地的NHibernate缓存。

In short, this solution doesn't offer ultimate control over your transactions, because it doesn't ever use more than one transaction. I guess I can accept that, since nested transactions are by no means universally supported in every DBMS anyway. But now perhaps I can at least write code without worrying about whether we're already in a transaction or not.

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

#4


1  

That implementation doesn't support nesting, if you want nesting use Ayende's UnitOfWork implementation. Another problem with the implementation your are using (at least for web apps) is that it holds onto the ISession instance in a static variable.

这个实现不支持嵌套,如果您想要嵌套使用Ayende的UnitOfWork实现。您正在使用的实现(至少对于web应用程序)的另一个问题是,它将ISession实例放在一个静态变量中。

I just rewrote our UnitOfWork yesterday for these reasons, it was originally based off of Gabriel's.

由于这些原因,我昨天才重新写了我们的单位,它最初是基于盖伯瑞尔的。

We don't use UnitOfWork.Current.BeginTransaction(), we use UnitofWork.TransactionalFlush(), which creates a separate transaction at the very end to flush all the changes at once.

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

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