来自脏检查的掩码属性

时间:2016-04-05 17:30:56

标签: nhibernate

我的所有表中都有一个名为LoggedInPersonID的列。为了避免混乱映射代码,Nhibernate Interceptor会覆盖OnFlushDirty和OnSave以自动分配LoggedInPersonID属性。

如果LoggedInPersonID是唯一更改的属性,我认为实体是干净的。目前,Nhibernate(理所当然地)认为该实体是脏的。

是否存在任何映射构造以从Nhibernate的脏检查中转义属性,同时仍包括任何插入/更新中的列?

或者,我考虑实现IPreUdateEventListener接口并使用OnPreUpdate事件来检查OldState和State之间的唯一区别是否属于LoggedInPersonID属性,如果是这种情况则取消更新。这是一种有效的方法吗?

2 个答案:

答案 0 :(得分:0)

更简单的案例

如果实体不脏,我宁愿尝试避免设置LoggedInPersonID。我不习惯从IPreUdateEventListener取消更新:仍然会发生很多其他处理,例如二级缓存更新和其他PostUpdate处理。

OnFlushDirty xml doc说明:

  

在冲洗期间检测到物体变脏时调用。

所以这意味着NHibernate甚至在设置LoggedInPersonID之前就认为你的对象是脏的。

您应该在具有条件断点的拦截器中检查它是否仅停留在有问题的实体类型上,并检查currentStatepreviousState之间是否已经有其他更改代码影响其LoggedInPersonID

也许你已经在其他地方设置了LoggedInPersonID的其他逻辑。

更难的案例

但是检查NHibernate代码,可能会有点混乱。在我看来,可以在可能很脏的实体上调用OnflushDirty。 (也许这"可能很脏"是由what I had suspected in my answer on your previous question引起的。)

我是这样的,你应该在你的拦截器里做自己的脏检查。您可以在OnFlushDirty中进行脏检查,但NHibernate仍会自行执行,导致脏检查两次。为了避免对每个实体进行两次脏检查,您需要先做好自己的想法:如果这是唯一的脏属性,则从脏检查中逐出LoggedInPersonID

NHibernate脏检查实现并不简单。比编写自己的脏检查更好地重用它。但这需要为拦截器添加一些代码。 (在this blog on NHibernate.info的帮助下完成。)

using NHibernate;
using NHibernate.Type;
using NHibernate.Proxy;
...

public class LoggedInPersonIDInterceptor : EmptyInterceptor
{
    ... 
    // your previous code handling the setting of LoggedInPersonID
    ...

    private ISession _session;
    public override void SetSession(ISession session)
    {
        _session = session;
    }

    public override int[] FindDirty(object entity, object id,
        object[] currentState, object[] previousState,
        string[] propertyNames, IType[] types)
    {
        var sessionImpl = _session.GetSessionImplementation();
        var persistenceContext = sessionImpl.PersistenceContext;
        var entry = persistenceContext.GetEntry(entity);
        if (entry == null)
        {
            // The blog post try to handle proxy case but that part looks
            // buggy to me. If you do not need to handle proxies, just let
            // default implementation do the job by returning null here.
            return null;
        }
        var persister = sessionImpl.Factory.GetEntityPersister(entry.EntityName);
        var dirtyPropertiesIndexes = persister.FindDirty(currentState,
            previousState, entity, sessionImpl);
        // Probable superfluous null check...
        if (dirtyPropertiesIndexes == null || dirtyPropertiesIndexes.Length != 1)
        {
            return dirtyPropertiesIndexes;
        }

        if (propertyNames[dirtyPropertiesIndexes[0]] == "LoggedInPersonID")
        {
            // return empty array for telling that nothing has changed
            return new int[] {};
        }

        return dirtyPropertiesIndexes;
    }
}

旁注:我已在other question revisions中看到您正在propertyNames[i].ToLower() == "loggedinpersonid"进行测试。如果您需要,我通常更喜欢这样做:StringComparer.OrdinalIgnoreCase.Equals(propertyNames[i], "LoggedInPersonID")。这可以避免在手动缩小属性名称时搞乱。

其他解决方案

也许this other way我后来发现会更容易。

答案 1 :(得分:0)

我认为如果你已经在OnSave中更改了属性,那么脏检查将会发生,最后OnFlushDirty将会发生,当它已经确定时。至少如果您(不必要地)在对象上调用Save()或SaveOrUpdate(),尽管它不是新创建的。