Mask properties from dirty check

左心房为你撑大大i 提交于 2019-12-23 05:44:26

问题


I have a column in all my tables called LoggedInPersonID. To avoid cluttering mapping code, an Nhibernate Interceptor overrides OnFlushDirty and OnSave to assign the LoggedInPersonID property automatically.

If LoggedInPersonID is the only property changed, I consider the entity clean. At the moment Nhibernate (rightfully) considers the entity to be dirty.

Does any mapping construct exist to escape a property from Nhibernate's dirty check, while still including the column in any inserts/updates?

Alternatively, I have considered implementing the IPreUdateEventListener interface and use the OnPreUpdate event to check whether the only difference between OldState and State is in the property LoggedInPersonID, and cancel the update if that is the case. Would that be a valid approach?


回答1:


I think if you already change the property in OnSave, the dirty check will come after, and finally OnFlushDirty will occur, when it is already decided. At least if you (unnecessarily) call Save() or SaveOrUpdate() on your object, although it is not a newly created one.




回答2:


Simplier case

I would rather try to avoid setting LoggedInPersonID if the entity is not dirty. I am not comfortable with cancelling the update from IPreUdateEventListener: a lot of other processing still occurs, like second level cache updating, and other PostUpdate processing.

OnFlushDirty xml doc states:

Called when an object is detected to be dirty, during a flush.

So this means NHibernate considers your object to be dirty even before you have set its LoggedInPersonID.

You should probably check that in your interceptor with a conditional break-point to stop only on your entity type having troubles, and check if there is already some other changes between currentState and previousState before your code affects its LoggedInPersonID.

Maybe have you by example some other logic elsewhere which has already set LoggedInPersonID.

Harder case

But checking NHibernate code, it could be a bit muddier. It looks to me like OnflushDirty could be called on entities which might be dirty. (And maybe this "might be dirty" is caused by what I had suspected in my answer on your previous question.)

I such case, you should do your own dirty check inside your interceptor. You may do the dirty check in your OnFlushDirty, but then NHibernate will still do its own, causing the dirty check to be done twice. To avoid dirty checking twice each entity, you need then do your first idea: evicting LoggedInPersonID from the dirty check if this is the only dirty property.

NHibernate dirty check implementation is not trivial. Better reuse it than coding your own dirty check. But this need adding some code to your interceptor. (Done with the help of 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;
    }
}

Side note : I have seen in your other question revisions your were testing on propertyNames[i].ToLower() == "loggedinpersonid". If you need that, I generally prefer do that this way : StringComparer.OrdinalIgnoreCase.Equals(propertyNames[i], "LoggedInPersonID"). This avoid messing up while manually lower-casing the property name.

Other solution

Maybe this other way I found later would be easier.



来源:https://stackoverflow.com/questions/36433215/mask-properties-from-dirty-check

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!