I've been working with NHibernate and ASP.NET and I've found quite some troubles to get NHibernate to work the way I wanted. We have an abstraction layer on top of NHibernate that hides the NHibernate intrinsics. With this abstraction layer we handle all the persistance for the domain entities.

The problem comes with the NHibernate session handling. We open a session per request (if needed) and we may store some entities in ASP.NET Session, Cache or Application. Basically, the problems present them selfs when we obtain an object with some NHibernate session, and later on we try to handle that object after the owning session has been closed. Example: put some object in Cache and try to access some collections later on.

Changing the current NHibernate session for an object proved to be quite troublesome (or... I really missed the best way to do it). We detected two scenarios where we needed to update the session for an NHibernate mapped class:

  • When we had an NHibernate proxy with a closed session
  • When we had the object, with some uninitialized NHibernate bags/sets/whatever - in this case we can work with the object, but we'll get an error when we try to access an uninitialized collection

These two scenarios will present the calling code with the following exception: Could not initialize proxy - the owning Session was closed.

I really, really searched the web to find some answers, but I got nothing. The NHibernate documentation suggests the use NHibernateUtil.Initialize to handle this problem, but this method assumes that the session's still available - and that may not be true.

To update the NHibernate proxy's session, we perform the following:

C#:
  1. void CheckProxySession(object entity, ISession target)
  2. {
  3.       INHibernateProxy proxy = entity as INHibernateProxy;
  4.     if (proxy == null) {
  5.         return;
  6.     }
  7.     if (!NHibernateUtil.IsInitialized(proxy)) {
  8.         LazyInitializer initializer = NHibernateProxyHelper.GetLazyInitializer(proxy);
  9.         initializer.Session = target.GetSessionImplementation();
  10.         initializer.Initialize();
  11.     }
  12. }

To update an object that's not a proxy and that have some uninitialized NHibernate containers:

C#:
  1. void CheckObjectSession(Loki.DataRepresentation.IEntity entity)
  2. {
  3.     ISession session = null;
  4.     try {
  5.  
  6.         session = GetSession();
  7.  
  8.         // hack to force NHibernate to associate the new session with the given entity
  9.         session.Update(entity);
  10.  
  11.     } catch {
  12.         if (session != null) {
  13.             session.Dispose();
  14.         }
  15.         throw;
  16.     }
  17. }

This will solve the lazy initialization issue, but it may bring some extra problems if you change the objects and don't update/flush. You may get an NHibernate exception if you try to associate a dirty object to a new session, or you may get some troubles with transaction related logic.

Does anyone know a better solution to this problem?

Related Posts

11 Responses to “NHibernate Sessions with ASP.NET”

  1. Nelson Correia Says:

    Can’t you hold the NHibernate session open during all the HTTP request lifetime?

    Check the Open Session in View pattern: http://www.hibernate.org/43.html

  2. Pedro Santos Says:

    Yes I can… that’s what I do. The problem comes when you try to use the object in a different request… have you really read the post? :)

  3. Paulo Pires Says:

    this should be taken care by your container. jboss deals with this by means of transactions. a section is opened when a business call is created, and is destroyed as soon as the business call is closed. a transaction is closed too :-)

  4. Nelson Correia Says:

    I must have missed the 2nd paragraph! :p

    I never did cache by myself with NHibernate, I’ve used a second-level NHibernate cache, that shares data between sessions (as long as sessions were created by the same SessionFactory). This could be used with ASP.NET cache or other cache providers and seems to make caches/session management transparent to the programmer.

    Have I missed your point again? :p
    Probably yes… I’m not any NHibernate expert. :)

  5. Christian Crowhurst Says:

    To associate a disconnected object (entity) to a new session I believe you need to call session.Lock() passing in the object that you want to associate with the session. For example:

    CurrentSession.Lock(yourEntity, LockMode.None);

  6. Steinar Dragsnes Says:

    Hi!

    I used to have problems with the session handling as well, but now I’m using Spring.Net’s HibernateTemplate and OSIV so it’s no problem for me anymore. Also there is tons of posts on Spring.Net’s user forum regarding these problems, take a look at:http://forum.springframework.net/forumdisplay.php?f=15

    Whenever I have a detached object and want to attach it to a new session (e.g when it comes back to the server), I simply use: session.Lock(entity, LockMode.Read); This is how the NHibernate documentation recommends reattachment if you haven’t modified the entity. If you have modified it, then session.update will reattach the entity and synchronize the changes into the new session. When you flush the session these changes will go into the DB.

    Take a look at the Spring.Net NHibernate forum, there is a lot of discussions, tips and recommendations on how to solve these common issues.

    Cheers,
    Steinar.

  7. superjason Says:

    Thank you, I’ve been battling with this for some time, and it’s hard to find the right thing to search for in Google.

  8. Batch Processing with NHibernate | Pedro Santos Says:

    [...] code is very slow. Some time ago, I had to go into NHibernate source code to understand how I could use sessions with ASP.NET, and I noticed that NHibernate uses a lot of internal objects. Well, on this case, I am fetching a [...]

  9. Gpg Says:

    i have the same difficult
    originaly my code was:
    Dim iSession As ISession = ConfigLoader.GetInstance.session
    Try : return iSession.Load(GetType(DataModel.clsContact), Id)
    Catch ex As Exception : Throw New clsException(ex)
    Finally : iSession.Disconnect()
    End Try

    but after testing, i discover that session use proxy for assignation. so if session was disconnected, proxy isnot aviable.

    we can turn around this difficult by instanciate a new object inside the session and return it like that:

    Dim iSession As ISession = ConfigLoader.GetInstance.session
    Try
    Dim obj As clsContact = iSession.Load(GetType(DataModel.clsContact), Id)
    Return obj ‘ceci est absolument nécessaire car l’objet n’est réellement instancié qu’au moment de l’assignation. or un return de l’objet = instanciation dans la couche suppérieur = isession déconnectée => plantage…
    Catch ex As Exception : Throw New clsException(ex)
    Finally : iSession.Disconnect()
    End Try

  10. NHibernate and ASP.Net: what’s the problem? « Hungry for Knowledge Says:

    [...] ASP.NET MVC application NHibernate Session Module opens unecessary transactions for CSS and images NHibernate Sessions with ASP.NET Posted by serge desmedt Filed in .NET, Software Design Tags: ASP.NET, [...]

  11. Scott Says:

    I’m kind of new to NHibernate and was wondering if someone could shed some light on the behavior that I’m experiencing as well as a possible solution.

    I have an NHibernate entity called User which upon login of my ASP.NET app, if valid, i retrieve and store in asp.net session. If null, I throw them back to the login page, etc…

    Well, on this User object there are some other associations that I want to traverse later on subsequent http requests but I get the exception as the proxy’s are no longer associated with the active session.

    (example: accessing User.UserCompanies[0].Company.Name will cause LazyInitializationException with no session associated)

    What is the best way to make these proxy’s “alive” again without forcing them to actually initialize? I’m using the Session-Per-Request pattern. I’d prefer not having to re-get the User based on ID each request but am open to the most efficient solution.