NHibernate审计跟踪不会导致"集合未被flush处理"错误

cbp*_*cbp 8 nhibernate

Ayende有一篇关于如何使用事件处理程序为NHibernate(这里)实现简单审计跟踪的文章.

不幸的是,正如在注释中可以看到的那样,他的实现导致抛出以下异常:集合xxx未由flush()处理

问题似乎是在脏属性上对ToString的隐式调用,如果dirty属性也是映射实体,则会导致麻烦.

我尽最大努力建立一个有效的实施但没有运气.

有谁知道一个有效的解决方案?

小智 9

我能够使用以下解决方法解决相同的问题:在侦听器中的当前持久性上下文中的所有集合上将processed标记设置为true

public void OnPostUpdate(PostUpdateEvent postEvent)
{
    if (IsAuditable(postEvent.Entity))
    {
       //skip application specific code

        foreach (var collection in postEvent.Session.PersistenceContext.CollectionEntries.Values)
        {
            var collectionEntry = collection as CollectionEntry;
            collectionEntry.IsProcessed = true;
        }

        //var session = postEvent.Session.GetSession(EntityMode.Poco);
        //session.Save(auditTrailEntry);
        //session.Flush();
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • 你知道什么(可能不需要的)副作用设置`IsProcessed`到'true`可能有什么? (6认同)

des*_*nit 8

修复应该如下.创建一个新的事件侦听器类,并从NHibernate.Event.Default.DefaultFlushEventListener派生它:

[Serializable] 
public class FixedDefaultFlushEventListener: DefaultFlushEventListener 
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    protected override void PerformExecutions(IEventSource session)
    {
        if (log.IsDebugEnabled)
        {
            log.Debug("executing flush");
        }
        try
        {
            session.ConnectionManager.FlushBeginning();
            session.PersistenceContext.Flushing = true;
            session.ActionQueue.PrepareActions();
            session.ActionQueue.ExecuteActions();
        }
        catch (HibernateException exception)
        {
            if (log.IsErrorEnabled)
            {
                log.Error("Could not synchronize database state with session", exception);
            }
            throw;
        }
        finally
        {
            session.PersistenceContext.Flushing = false;
            session.ConnectionManager.FlushEnding();
        }

    }
} 
Run Code Online (Sandbox Code Playgroud)

在NHibernate configuraiton中注册它:

cfg.EventListeners.FlushEventListeners = new IFlushEventListener[] { new FixedDefaultFlushEventListener() };
Run Code Online (Sandbox Code Playgroud)

您可以在Hibernate JIRA中阅读有关此错误的更多信息:https: //hibernate.onjira.com/browse/HHH-2763

NHibernate的下一个版本应该包含该修复.

  • 究竟.它应该被集成到DefaultAutoFlushEventListener中,在NHibernate发布中没有好运.请参阅:https://github.com/nhibernate/nhibernate-core/pull/143 (2认同)

Ste*_*ger 4

这一点都不容易。我写了这样的东西,但它非常适合我们的需求,而且不是微不足道的。

一些额外的提示:

您可以使用以下命令测试引用是否已加载

NHibernateUtil.IsInitialized(entity)
Run Code Online (Sandbox Code Playgroud)

或者

NHibernateUtil.IsPropertyInitialized(entity, propertyName)
Run Code Online (Sandbox Code Playgroud)

您可以将集合投射到IPersistentCollection. 我实现了一个IInterceptor获取每个属性的 NHibernate 类型的位置,我不知道在使用事件时可以在哪里获取它:

if (nhtype.IsCollectionType)
{
    var collection = previousValue as NHibernate.Collection.IPersistentCollection;
    if (collection != null)
    {
        // just skip uninitialized collections
        if (!collection.WasInitialized)
        {
            // skip
        }
        else
        {
            // read collections previous values
            previousValue = collection.StoredSnapshot;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当您从 NHibernate 获取更新事件时,实例就会被初始化。您可以安全地访问基本类型的属性。当您想要使用 时ToString,请确保您的ToString实现不会访问任何引用的实体或任何集合。

您可以使用 NHibernate 元数据来确定类型是否映射为实体。这对于在对象模型中导航可能很有用。当您引用另一个实体时,当它发生更改时,您将收到有关该实体的其他更新事件。