This is post #16 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.
In my last post I waxed lyrical about the importance of optimistic concurrency. But is it actually working in my web application? Time to check.
The only screen I have that lets me edit anything is the one I just created, the user profile screen. I'll temporarily make the version property visible for my testing and open up two screens to simulate two users modifying the same data.
Both have the same version number, as they should. I'll modify one to increment its version...
Now I'll modify the other screen and expect a stale object exception...
But instead I get another version increment. NHibernate has ignored the "1" I gave it, used the "2" it got from the re-retrieval, and incremented it to "3". Oh no... NHibernate, what is going on?!?
This isn't a "bug" as such. NHibernate just doesn't expect to be used this way, i.e. retrieving an object and changing its version. It thinks of version as a managed property, fundamental to the way caching and all that other hidden goodness inside of NHibernate works. Setting version doesn't really do anything because NHibernate already knows what the version is -- it's simply not listening when anyone tries to tell it different.
The problem has been noticed before and talked about at length in respect to both Hibernate and NHibernate.
There are two ways to make a version change "stick". One is to evict the object from NHibernate, which prevents NHibernate from knowing what the version should be and forces it to look in the version property instead. The other is perform a manual check on the version property in an NHibernate interceptor and throw a stale object exception from there. I've gone down the eviction route before and run into problems, so the solution I favour now is the interceptor.
Luckily there's already some code in Moja to help build out interceptors. That's amazing! It's like I knew I would be making interceptors at some point...
namespace StackingCode.Moja.Repositories.NHibernate.Interceptors
{
public class StaleInterceptor : Interceptor
{
public override bool OnUpdate(object entity, object id, IEnumerable<EntityPropertyForUpdate> properties)
{
CheckStaleness(entity, id);
// No properties have changed, return false.
return false;
}
public override bool OnDelete(object entity, object id, IEnumerable<EntityProperty> properties)
{
CheckStaleness(entity, id);
// No properties have changed, return false.
return false;
}
private void CheckStaleness(object entity, object id)
{
ISessionImplementor sessionImplementor = InterceptorAdapter.Session.GetSessionImplementation();
IEntityPersister entityPersister = sessionImplementor.GetEntityPersister(null, entity);
if (entityPersister.IsVersioned)
{
object version = entityPersister.GetVersion(entity, sessionImplementor.EntityMode);
object currentVersion = entityPersister.GetCurrentVersion(id, sessionImplementor);
if (!entityPersister.VersionType.IsEqual(version, currentVersion))
throw new StaleObjectStateException(entityPersister.EntityName, id);
}
}
}
}
The staleness code is almost a straight copy from Stefan's example. Here I'm delving into the innards of NHibernate to get the version on the entity itself, and the current version according to NHibernate, and comparing the two. This is called right before any object is updated or deleted. Throwing an exception will prevent the update or delete from taking place.
Hooking the interceptor up to the session is just a small matter of configuring Spring a bit more...
<object name="Session" factory-object="SessionFactory" factory-method="OpenSession" scope="request">
<constructor-arg>
<list>
<ref object="StaleInterceptor" />
</list>
</constructor-arg>
</object>
<object name="StaleInterceptor" type="StackingCode.Moja.Repositories.NHibernate.Interceptors.StaleInterceptor, StackingCode.Moja.Repositories.NHibernate" scope="request" />
<object name="SessionFactory" type="StackingCode.Moja.Repositories.NHibernate.SessionFactoryWrapper, StackingCode.Moja.Repositories.NHibernate" />
Going back to my two screens. If I update one...
And then the other...
Success! A stale object exception. What the user is supposed to do with it though... Well, that's a story for another day.
There are 0 comments.
Older
User Profile
Older
User Profile
browse with Pivot
Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue
Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)
Comments
Leave a Comment
Please register or login to leave a comment.