NHibernate子会话未正确刷新

NHibernate子会话未正确刷新,nhibernate,Nhibernate,我们通过NHibernate事件监听器实现了一个审计系统。在监听器中,我们跟踪所有更改并将其写入审计表。为了尽量提高性能,我们对审计表使用了Guid,以便尽可能多地批处理更新 我们正在编写“子会话”的更新,我们得到如下结果: protected ISession GetSession(AbstractEvent @event) { if (@event == null) { throw new ArgumentNullException("event");

我们通过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();
    }
我知道这不是你的普通问题,但如果有人有任何经验或建议分享,我很乐意听到

我离将审计记录写入队列还有2秒钟,但我想在放弃之前用尽一切可能

提前感谢,


Steve

问题来自于
刷新模式。提交
:在这种模式下,NHibernate在提交事务时只刷新会话一次。因此,在刷新之后,它将不再刷新,刷新之后的任何更改都不会被刷新


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

我们以相同的方式处理审核(使用GUID PK)。使用Identity PK generator时,每次保存调用都会立即发出插入,但使用GUID时,插入仅在刷新期间执行。我们在几年前通过修补NHibernate源代码解决了这个问题。在Flush方法的SessionImpl.cs中(第1467行附近),我添加了以下内容:

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