NHibernate:在提交事务之前调用Session.Flush()是否有效?

时间:2011-03-28 21:13:54

标签: c# .net nhibernate transactions flush

我需要在NHibernate的工作单元中执行两个操作:事务性操作(实体保存)和非事务性操作。

由于非事务性操作无法回滚,如果我在执行非事务性操作(并最终提交事务)之前保存实体,我仍然会收到事务性行为:

  • 仅当两个子操作成功执行时才会提交操作;
  • 如果实体保存失败,则不会执行非事务性操作;
  • 如果非事务性失败,则实体保存将被回滚。

问题:使用如下代码,NHibernate将不会执行实际的sql插入,直到调用transaction.Commit()(在内部调用session.Flush()):

        using (var transaction = session.BeginTransaction())
        {
            session.Save(entity);
            NotTransactionalOperationBasedOn(entity);

            transaction.Commit(); // the actual sql insert will be executed here
        }

使用这样的代码,如果Sql Insert失败则为时已晚:NotTransactionalOperation已被执行。要在执行NotTransactionalOperation之前执行实际的SQL插入,我必须在session.Save()之前明确调用session.Flush():

        using (var transaction = session.BeginTransaction())
        {
            session.Save(entity);
            session.Flush(); // the actual sql insert will be executed here

            NotTransactionalOperationBasedOn(entity);

            transaction.Commit(); 
        }

代码有效,但......在提交事务之前调用session.Flush()是最佳做法吗?如果没有,是否有更好的方法来实现相同的结果?

2 个答案:

答案 0 :(得分:3)

Flushing意味着NHibernate将确保所有更改都持久保存到DB。也就是说,它将确保执行所有必需的SQL语句。 当事务失败并因此回滚时,所有​​这些更改都将被还原。所以,我认为没有问题(你必须记住,你的实体可能没有处于有效状态,因为交易失败了。)

但是......我实际上没有看到问题: 当非事务性过程失败时,问题是什么?由于该程序是非事务性的,我想它对数据库所持有的信息没有影响?或者,这段代码做了什么?

如果是非交易性的,为什么不能在工作单元之外进行?

当保存失败时,我猜你会确保不仅事务被回滚,而且异常也将被抛出到堆栈上,直到它遇到错误处理程序,这可能意味着你的非交易代码也不会被执行:

using( var transaction = session.BeginTransaction() ) 
{
      repository.Save (entity); 

      transaction.Commit();   // error, exception is thrown.
}

NonTransactionalCode (entity); // this line will not be executed, since the exception will be thrown up the stack until a suitable catch block is encountered.

答案 1 :(得分:1)

这是不寻常的,但是如果你想在工作单元之外做某些事情,这取决于内部完成的事情,那就完全有效了。然而,问题就变成了,如果操作单元依赖于内部的某些操作,为什么操作在工作单元之外执行?如果它完全依赖于顺序,那么在执行非事务性操作之前,您应该考虑刷新AND提交。如果非trans操作确定整个事物是否将提交(例如,它可能抛出异常),那么它应该是工作单元的一部分。

非trans选项,可能不应该依赖于DB中的数据,除非它依赖于像触发器这样的数据层代码(虽然我喜欢触发器,但是出于几个原因通常应该避免使用它们,其中最重要的是有这种复杂的保存操作的倾向)。除了作为最终选项之外,您通常应该将业务逻辑保留在应用程序层中。如果对象已保存到会话中,则该会话所在的任何位置都可以使用该数据,并且该会话应该根据需要进行广泛确定(或者考虑与可以访问父级的子级的共享“父”会话)所有时间)。