Login

Username

Password





Register   Reset password

Developer resources

Developer documents

Developer articles

NHibernate and Cuyahoga part 1: configuration and session management.

This is a first article in a series where I’m going to try and explain how the Cuyahoga web site framework uses NHibernate for object persistence. The versions being used in this article series are 0.7.0.1 for Cuyahoga and 0.7 for NHibernate.

Configuration

So let’s start with the NHibernate configuration. This is the place where mappings between objects and the database live at run time. In Cuyahoga, this configuration is encapsulated with the Cuyahoga.Core.Service.SessionFactory class. This class serves as a wrapper for the NHibernate Configuration and SessionFactory classes. It is designed as a singleton because building the configuration is expensive and all application threads can safely use the same instance. At creation time the configuration for the Cuyahoga core classes is built and an ISessionFactory instance is created:

Configuration config = new Configuration();
this._nhibernateConfiguration = config.AddAssembly(this.GetType().Assembly);
this._nhibernateFactory = this._nhibernateConfiguration.BuildSessionFactory();

Since the core domain classes are in the same assembly as the SessionFactory, we can add them all at once to the configuration by simply adding the assembly (this.GetType().Assembly). Note that the Cuyahoga SessionFactory keeps a reference to the NHibernate configuration. The docs say that this one is to be discarded, but we need it for adding the mappings of the separate Cuyahoga modules.
Most modules have their own mappings but they are not loaded until they are first requested, so it’s impossible to build the configuration for the core classes and the modules all at once at creation time. The RegisterPersistentClass() method makes it possible for modules to still join the mapping party: 

/// <summary>
/// Add a class to the NHibernate mappings.
/// If the class already is mapped, nothing will happen.
/// </summary>
/// <param name="type"></param>
public void RegisterPersistentClass(Type type)
{
if (this._nhibernateConfiguration.GetClassMapping(type) == null)
{
// Class isn't mapped yet, so do it now.
this._nhibernateConfiguration.AddClass(type);
this._classesAdded = true;
}
}

Note that the NHibernate SessionFactory isn’t recreated immediately. Modules may add multiple classes to the configuration in one go but they also have the responsibility to call the Rebuild() method when done.

Session management

After configuration we have everything in place for the real action. With the GetSession() method, an open NHibernate session can be obtained to do the persistence of objects.

Cuyahoga doesn’t use ‘raw’ NHibernate sessions though for persisting the core objects, but has a façade class Cuyahoga.Core.Service.CoreRepository that hides almost all NHibernate specific things for the rest of Cuyahoga. This class contains generic methods for retrieving, saving, updating and deleting objects of a specific type and some methods for more specific operations (like filtering and sorting). Internally, CoreRepository uses a NHibernate session that is obtained from the SessionFactory.

 The session management follows the pattern that is called ‘Session per request’. At the beginning of each request, a single NHibernate session is created that is used during the entire ASP.NET page lifecycle. Normally this is done by storing the session in the HttpContext.Current.Items collection. Cuyahoga does it a little different by storing an instance of the CoreRepository class, but conceptually this is more or less the same.

There is a dedicated HttpModule that deals with storing the CoreRepository to the request: Cuyahoga.Web.Util.NHSessionModule. There is a BeginRequest event handler that creates the CoreRepository instance and an EndRequest event handler that closes the session:

private void Context_BeginRequest(object sender, EventArgs e)
{
// Create the repository for Core objects and add it to the current HttpContext.
CoreRepository cr = new CoreRepository(true);
HttpContext.Current.Items.Add("CoreRepository", cr);
}
private void Context_EndRequest(object sender, EventArgs e)
{
// Close the NHibernate session.
if (HttpContext.Current.Items["CoreRepository"] != null)
{
CoreRepository cr = (CoreRepository)HttpContext.Current.Items["CoreRepository"];
cr.CloseSession();
}
}
 You could also put this in the Global.asax, but that’s a matter of personal preferences. I just happen to like HttpModules.
Now, the ASP.NET pages and user controls in Cuyahoga can access the CoreRepository at any time during the page lifecycle.

The CoreRepository doesn’t have to offer much functionality for the modules, so they have to manage the persistence themselves. Examples of this can be found in the classes that inherit from Cuyahoga.Core.Domain.ModuleBase like Cuyahoga.Modules.Articles.ArticleModule or Cuyahoga.Modules.StaticHtml.StaticHtmlModule.

It’s important that the modules use the same session as the Cuyahoga.Web.UI.PageEngine class that builds the pages. They can obtain that session from the CoreRepository that is in the Context.Items collection. Currently however, they raise an event when they need a session (OnNHSessionRequired) that is handled by the PageEngine. This is a little heritance from earlier days when Cuyahoga didn’t use the Context.Items to store the session. To be refactored…;).

4/26/2005 12:36:00 PM Published by Martijn Boland Category Developers Comments 6

Comments
  • I have been looking forward for articles on Cuyahoga for long time, it's a good starting, keep writing.
    thx.

    by bestcomy - 4/29/2005 8:13:56 AM
  • Thanks for this first article it was very helpfull. Could you write an article explaining the method you use to create 'nodes' in Cuyahoga and how your approach differs other opensource portals.

    by Jeremy Maynor - 5/2/2005 10:18:37 AM
  • Hmm, that would be something nice to write about as well, but first I'd like to write a little bit more about the NHibernate stuff because that's what most people are interested in.

    by Martijn Boland - 5/4/2005 2:28:32 PM
  • Well written and very interesting. Looking forward to reading more of this!

    by Lars Arne Brekken - 5/14/2005 5:39:00 PM
  • About the "Session per request":

    I thought the best session usage is to close an ISession as soon as possible, as doesn't it leave a DB connection open?

    I do, however, like the HttpModule approach, and the opening and closing of a session on the begin and end request is very elegant.

    I guess I'm curious how this would perform under a heavy load.

    by Andrew Hallock - 6/22/2005 4:21:39 PM
  • I've actually been curious about this topic for some time. I notice there are the Reconnect and Disconnect methods on ISession, which do open and close the IDbConnection, but not the ISession itself (thus, saving the ISession object cache).

    Hmmmm, perhaps this is a job for AOP? :) Certain methods would use an active ISession, and before and after that call there could be advice to invoke Reconnect() and Disconnect(), or for the first use of ISession, call Open(). And then on the end request call Close()...sorry for the stream of consciousness.

    by Andrew Hallock - 6/22/2005 6:13:01 PM

Back