【问题标题】:NHibernate Child Session not flushing properlyNHibernate 子会话未正确刷新
【发布时间】:2010-07-21 18:39:00
【问题描述】:

我们已经通过 NHibernate 事件监听器实现了一个审计系统。在我们的侦听器中,我们跟踪所有更改并将它们写到我们的审计表中。为了尽量提高性能,我们在审计表中使用了 Guid,以便我们可以尽可能多地批量更新。

我们正在写出对“子会话”的更新,如下所示:

 protected ISession GetSession(AbstractEvent @event)
 {
     if (@event == null)
     {
        throw new ArgumentNullException("event");
     }

        ISession childSession = @event.Session.GetSession(EntityMode.Poco);

        return childSession;
 } 

根据 NHibernate 文档,该会话应该是一个“子”会话,它继承其父级的所有属性 - 包括事务。

创建实体后,我们将其保存到会话中:

childSession.Save(auditLogEntry);

所有这些都是在事务中调用的,我希望对 childSession 所做的更改会在事务提交后刷新。不幸的是,什么都没有发生,更改也没有刷新。

应该注意的是,我可以在保存后立即使用手动刷新,但这对我们不起作用,因为更改将不再被批处理(这将产生不可接受的性能)。

起初我认为这种行为仅限于事件,但我能够将其抽象为单元测试以复制行为。

  public void When_Saving_Audit_Log_Records_To_Child_Session_Flushes_When_Transaction_Committed()
    {
        ISession session = GetSession();
        session.FlushMode = FlushMode.Commit;

        ITransaction transaction = session.BeginTransaction();

        ISession childSession = session.GetSession(EntityMode.Poco);

        AuditLogEntry entry = CreateAuditLogEntry();
        entry.AddAuditLogEntryDetail(CreateAuditLogEntryDetail());
        entry.AddAuditLogEntryDetail(CreateAuditLogEntryDetail());

        childSession.Save(entry);
        transaction.Commit();
    }

protected ISession GetSession()
    {
        return _sessionFactory.OpenSession();
    }

我知道这不是您的 NHibernate 问题,但如果有人有任何经验或建议可以分享,我很想听听。

我距离将审计记录写入队列还有 2 秒的时间,但我想在放弃之前用尽所有可能性。

提前致谢,

史蒂夫

【问题讨论】:

    标签: nhibernate


    【解决方案1】:

    问题来自FlushMode.Commit:在这种模式下,NHibernate 只会在提交事务时刷新会话一次。所以刷新后不会再次刷新,刷新后的任何更改都不会被刷新。

    要解决此问题,您可以手动刷新会话,或更改为FlushMode.Auto。但是,如果您使用 Auto,请注意带有事件侦听器和/或拦截器的 StackOverflowException,因为使用 Auto,NHibernate 将在查询之前刷新,结果调用 OnFlushDirty,因此如果您在 OnFlushDirty 中查询某些内容,它将触发另一个冲洗,然后在一个永无止境的循环中再次调用OnFlushDirty。为防止出现这种情况,您必须将 FlushMode 临时更改为 Never,或者实施一个系统来确定已处理哪些更改以避免一遍又一遍地处理相同的更改。

    【讨论】:

      【解决方案2】:

      我们以相同的方式处理审核(使用 GUID PK)。当使用 Identity PK 生成器时,每次对 Save 的调用都会立即发出一个 INSERT,但是当使用 GUID 时,INSERT 只会在 Flush 期间执行。我们在几年前通过修补 NHibernate 源解决了这个问题。在 Flush 方法的 SessionImpl.cs 中(大约第 1467 行),我添加了以下内容:

      // Flush children when parent is flushed.
      if (childSessionsByEntityMode != null) {
          foreach (var childSession in childSessionsByEntityMode) {
              childSession.Value.Flush();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-11
        • 1970-01-01
        • 2022-01-12
        • 1970-01-01
        • 2019-10-16
        • 2011-09-04
        • 2013-05-10
        相关资源
        最近更新 更多