tags:

views:

32

answers:

1

Hi,

This leads to a StaleStateException exception:

var entity = Session.Load(id);
Session.Evict(entity);
entity.SomePropert = "Something";
entity.Version = Version--;
Session.SaveOrUpdate(entity); // Throws Exception

This however does not throw an exception. Also no row is updated.

var entity = Session.Load(id);
entity.SomePropert = "Something";
entity.Version = Version--;
Session.SaveOrUpdate(entity); // Does not throw Exception, 
                              // but no rows are updated
+1  A: 

The short answer is because version is a property that is managed by NHibernate. You can change it, but your changes are ignored. As you have found, one way to make the change stick is to evict the object and then reattach/save/update. The eviction causes NHibernate to forget what its value for the version is which makes it get it from the object. (I may be oversimplifying here.)

This issue has been raised before. Lots of people ask why anyone would want to do this -- one valid scenario is a stateless web application. Each HTTP request creates its own NHibernate session, which dies when the request is done. A page may be requested that shows the partial details of an object for editing. The ID and version are held in hidden fields. On postpack (which is a new request, only with data this time), a new NHibernate session reloads the object in question, applies the changes from the form data, including the version, and then saves the object. Modifying the version here in this scenario is completely valid. If someone else has modified the same object between the initial request and the postback, those changes should not be overwritten. A stale object exception should be thrown, but won't be, because the version change is ignored by NHibernate.

I know of two ways to fix the version "bug" for these kinds of scenarios. One is the eviction method already illustrated by the OP. The second is to create a NHibernate interceptor to catch update operations and check the version manually.

This issue and the two solutions are talked about here.

https://forum.hibernate.org/viewtopic.php?t=977889

I use an interceptor modified from the code provided here.

http://weblogs.asp.net/stefansedich/archive/2008/10/01/set-the-value-of-a-version-column-in-nhibernate-manually.aspx

The guts of my interceptor look slightly different (I use NHibernate 2.1.2.4000).

private void CheckVersion(object domainObject, object uid)
{
    ISessionImplementor sessionImpl = Session.GetSessionImplementation();
    IEntityPersister persister = sessionImpl.GetEntityPersister(null, domainObject);

    if (persister.IsVersioned)
    {
        EntityMode mode = Session.GetSessionImplementation().EntityMode;
        object version = persister.GetVersion(domainObject, mode);
        object currentVersion = persister.GetCurrentVersion(uid, sessionImpl);

        if (!persister.VersionType.IsEqual(version, currentVersion))
            throw new StaleObjectStateException(persister.EntityName, uid);
    }
}
Adam Boddington