Click here to Skip to main content
Click here to Skip to main content

Keeping Entity State Over Multiple NHibernate Sessions

By , 11 Sep 2010
Rate this:
Please Sign up or sign in to vote.

Introduction

This article and the attached ready to use code demonstrates how you can maintain an entity state over multiple NHibernate sessions.

Would you want to use this source?

You'll want to use this source if you are using NHibernate and need your entities to maintain their state through long conversations, over multiple NHibernate sessions. You might also want to use it if you want built-in auditing power and general state oriented methods implemented once and kept through the help of NHibernate.

The problem

NHibernate keeps track of entities within an NHibernate session (ISession). This means that if you fetch entities within a session, change those entities, and flush the session, the session would know exactly what you changed, which works very well in most cases – when it is possible to re-fetch an entity within any new session; but sometimes this isn't enough. For example: multiple views that modify the same entity instance, when any one of the views has a Save button, which is the time that the entity will actually be saved to the database. In this case, we can't re-fetch the entity because it was changed and the database (or cache) version is irrelevant. People will tell you – use a DTO; well, sometimes you got to use a DTO (when serializing, for example), but here, if we could just modify our previously fetched entity (it was fetched within an already disposed session), each time attaching it to the current session (to enable lazy loading), and at the end, saving the changes with NHibernate, knowing exactly what was changed (for dynamic-update, for example), wouldn't it be great?

The solution - high-level

Use dynamic proxies that keep the state of the entities. This way, the entities are ignorant of their state being kept, while the proxies are in charge of keeping the entities' state, and the NHibernate session interceptor is in charge of using those proxies, to find out the previous state of an entity, so it is kept through multiple sessions. The only catch – each entity, when being instantiated, must be wrapped with a proxy.

The usage: Two main classes concern the end-developer

  1. NHibernateInterceptor – Implementer of the NHibernate.IInterceptor interface, which intercept certain events of the session (like FindDirty).
  2. How should it be used?

    When creating a session, the interceptor should be injected into the session. The interceptor also gets the session factory as a c'tor argument. The following code demonstrates its usage:

    ISession session = 
       sessionFactory.OpenSession(new NHibernateInterceptor(sessionFactory))
  3. StatefullEntityProxyFactory – A factory to be used when instantiating an entity.
  4. This class is responsible for creating a proxy from an existing, or non existing entity instance. The proxy will appear to the application as if it is the actual entity, with one exception – the GetType() method. The proxy, although appears as the actual entity, can also be looked at as the stateful entity proxy.

    How should it be used?

    Instantiating an entity with a default 0-params c'tor:

    Entity entity = 
      new StatefullEntityProxyFactory().InstantiateEntityProxy< Entity >();
    ////< Set values, do stuff >
    //session.SaveOrUpdate(entity);

    Instantiating a proxy on an existing entity

    Entity entity = new Entity(1,"name");
    entity = 
      new StatefullEntityProxyFactory().InstantiateEntityProxy(entity) as Entity;

    The above two examples are the use cases for instantiating a proxy instead of an entity so that its state will be kept. These examples are shown for creating new entities. When an entity is fetched with NHibernate, the NHibernateInterceptor will do the same so that the state will be kept for the NHibernate fetched entities.

OK, but why does it work?

  1. The StatefullEntityProxyInterceptor class is a LinFu dynamic proxies interceptor, which holds a reference to a StateKeeper instance and to an entity. It forwards all the calls (properties and methods) to the underlying entity, except for a call to get the StateKeeper. This call is possible if we do:
  2. IStatefullProxy proxy = (IStatefullProxy)entity;
    StateKeerp stateKeeper = proxy.StateKeeper;
  3. IStatefullProxy is an interface we're making the proxy implement (using the LinFu proxies API), and that defines a property of type StateKeeper.
  4. The StateKeeper class has a previous state and a current state, and implements a FindDirty method that finds the dirty properties (by comparing the previous and current states). The NHibernate interceptor, in its OnLoad event, will set the previous state of the entity, and in the PostFlush, it will call the InitState method of the state keeper. In FindDirty, it will of course use the FindDirty method. Here is an example (from the OnLoad event):
  5. IStatefullProxy proxy = (IStatefullProxy)entity;
    proxy.StateKeeper.InitState(propertyNames, state);

To fully understand how it works, it is recommended that you setup a small application with a couple of entities, map them with NHibernate, instantiate them with the above described proxy factory, create the session injecting the interceptor, put breakpoints in every method of the NHibernateInterceptor, and watch what's going on when you save\update\fetch\attach your entities to a different session.

What else can be done with this idea?

Well, lots of stuff. An example of a generic IsModified method is there – just get the underlying StateKeeper as shown in the examples above, and call IsModified. FindDirty, which returns the dirty property names, can be useful for auditing as well, and the idea of IsModified can also be extended to implement RejectChanges.

Technologies used by this source

NHibernate 3.0 Alpha2, LinFu dynamic proxies. The source was not tested against NHibernate 2.1, but should work as no NH3 specific feature is being used.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

nadavsof
Architect
Israel Israel
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 11 Sep 2010
Article Copyright 2010 by nadavsof
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid