Wednesday, November 12, 2008

NHibernate onFlushDirty has null previousState

The problem

We use an Entity interceptor to create audit records when we update most tables. We also do a lot of our data access through .NET remoting. This means that we open a session to load the object, the object is detached, serialized, modified, then reattached to the session when we want to persist the changes.

We noticed that in the cases where we are updating detached objects, although the update was being applied correctly there were no audit records appearing in the database. On investigation we realised that the previousState parameter passed into the onFlushDirty method of our interceptor was null.


In the Save method of our data access class we were using the SaveOrUpdate method of the NHibernate session to persist the changes. Section 10.7 of the documentation states:

saveOrUpdate() does the following:

  • if the object is already persistent in this session, do nothing
  • if another object associated with the session has the same identifier, throw
    an exception
  • if the object has no identifier property, save() it
  • if the object's identifier has the value assigned to a newly instantiated
    object, save() it
  • if the object is versioned (by a or ), and
    the version property value is the same value assigned to a newly instantiated
    object, save() it otherwise update() the object

and merge() is very different:

  • if there is a persistent instance with the same identifier currently
    associated with the session, copy the state of the given object onto the
    persistent instance
  • if there is no persistent instance currently associated with the session,
    try to load it from the database, or create a new persistent instance
  • the persistent instance is returned
  • the given instance does not become associated with the session, it remains

As our detached objects had an Id and version when we called SaveOrUpdate() they were being updated, and seeing as all update() does is reattach an object to the session, the session had no record of the changes that had been made to that object since it was loaded.

The solution

The solution was to use the merge() method (new in NHibernate 2.0). Merge() checks the first level cache to see if an object with the given identifier has previously been loaded. If so it loads that object out of the first level cache and updates it's properties using the detached object. This means that the session is now able to track the changes made to the object so that when the flush occurs the previousState is no longer null.

Labels: , ,


Anonymous Ross said...

So funny, I Worked through this with you at the time and completly forgot about the solution. Then came accross it today on another project. Searched for it and low and behold your site came up! Thanks god you record stuff

3:22 pm  

Post a Comment

<< Home