Woah, Nellie.NET!

From my simple mind to… mine…

Simple NHibernate Example, Part 4 : Session Management

with 2 comments

Before continuing with the implementation, let’s talk briefly about NHibernate session management. This will be a short discussion, as you can get more details from chapter 8 of the book, Hibernate In Action. Also, please review the article, NHibernate Best Practices, for further explanation of the Session In View.

One of the potential problems with using NHibernate is the intermingling of managing the NHibernate sessions and transactions with the basic interactions between the domain and NHibernate. Ideally, we’d like the client using NHibernate to manage transactions outside of the context of the business workflow, committing or rolling back entire transactions depending on the results of the workflow (this is the unit of work pattern).

We’d like to be able to manage a single session across a single persistence context (or transaction context). We’ll utilize the thread safe CallContext object in an NHibernate SessionManager class. An upcoming post will show how this session manager works. In the meantime, here is the code (borrowed from NHibernate Best Practices):

using System.Configuration;

using System.Runtime.Remoting.Messaging;

using System.Web;

using NHibernate;

using NHibernate.Cache;

using Configuration=NHibernate.Cfg.Configuration;

using NHibernateConfiguration = NHibernate.Cfg;

namespace DealerMatrix.Data.NHibernate.Session

{

/// <summary>

/// Handles creation and management of sessions and transactions. It is a singleton because

/// building the initial session factory is very expensive. Inspiration for this class came

/// from Chapter 8 of Hibernate in Action by Bauer and King. Although it is a sealed singleton

/// you can use TypeMock (http://www.typemock.com) for more flexible testing.

/// </summary>

public sealed class NHibernateSessionManager

{

private ISessionFactory sessionFactory;

#region Thread-safe, lazy Singleton

/// <summary>

/// This is a thread-safe, lazy singleton. See http://www.yoda.arachsys.com/csharp/singleton.html

/// for more details about its implementation.

/// </summary>

public static NHibernateSessionManager Instance

{

get { return Nested.nHibernateNHibernateSessionManager; }

}

/// <summary>

/// Initializes the NHibernate session factory upon instantiation.

/// </summary>

private NHibernateSessionManager()

{

InitSessionFactory();

}

/// <summary>

/// Assists with ensuring thread-safe, lazy singleton

/// </summary>

private class Nested

{

static Nested()

{

}

internal static readonly NHibernateSessionManager nHibernateNHibernateSessionManager = new NHibernateSessionManager();

}

#endregion

private void InitSessionFactory()

{

Configuration cfg = new Configuration();

// The following makes sure the the web.config contains a declaration for the HBM_ASSEMBLY appSetting

if (ConfigurationManager.AppSettings[“HBM_ASSEMBLY”] == null ||

ConfigurationManager.AppSettings[“HBM_ASSEMBLY”] == “”)

{

throw new ConfigurationErrorsException(“NHibernateManager.InitSessionFactory: \”HBM_ASSEMBLY\” must be “ +

“provided as an appSetting within your config file. \”HBM_ASSEMBLY\” informs NHibernate which assembly “ +

“contains the HBM files. It is assumed that the HBM files are embedded resources. An example config “ +

“declaration is <add key=\”HBM_ASSEMBLY\” value=\”MyProject.Core\” />”);

}

cfg.AddAssembly(ConfigurationManager.AppSettings[“HBM_ASSEMBLY”]);

sessionFactory = cfg.BuildSessionFactory();

}

/// <summary>

/// Allows you to register an interceptor on a new session. This may not be called if there is already

/// an open session attached to the HttpContext. If you have an interceptor to be used, modify

/// the HttpModule to call this before calling BeginTransaction().

/// </summary>

public void RegisterInterceptor(IInterceptor interceptor)

{

ISession session = threadSession;

if (session != null && session.IsOpen)

{

throw new CacheException(“You cannot register an interceptor once a session has already been opened”);

}

GetSession(interceptor);

}

public ISession GetSession()

{

return GetSession(null);

}

/// <summary>

/// Gets a session with or without an interceptor. This method is not called directly; instead,

/// it gets invoked from other public methods.

/// </summary>

private ISession GetSession(IInterceptor interceptor)

{

ISession session = threadSession;

if (session == null)

{

if (interceptor != null)

{

session = sessionFactory.OpenSession(interceptor);

}

else

{

session = sessionFactory.OpenSession();

}

threadSession = session;

}

return session;

}

public void CloseSession()

{

ISession session = threadSession;

threadSession = null;

if (session != null && session.IsOpen)

{

session.Close();

}

}

public void BeginTransaction()

{

ITransaction transaction = threadTransaction;

if (transaction == null)

{

transaction = GetSession().BeginTransaction();

threadTransaction = transaction;

}

}

public void CommitTransaction()

{

ITransaction transaction = threadTransaction;

try

{

if (transaction != null && !transaction.WasCommitted &amp;& !transaction.WasRolledBack)

{

transaction.Commit();

threadTransaction = null;

}

}

catch (HibernateException ex)

{

RollbackTransaction();

throw ex;

}

}

public void RollbackTransaction()

{

ITransaction transaction = threadTransaction;

try

{

threadTransaction = null;

if (transaction != null && !transaction.WasCommitted &amp;& !transaction.WasRolledBack)

{

transaction.Rollback();

}

}

catch (HibernateException ex)

{

throw ex;

}

finally

{

CloseSession();

}

}

/// <summary>

/// If within a web context, this uses <see cref=”HttpContext” /> instead of the WinForms

/// specific <see cref=”CallContext” />. Discussion concerning this found at

/// http://forum.springframework.net/showthread.php?t=572.

/// </summary>

private ITransaction ThreadTransaction

{

get

{

if (IsInWebContext())

{

return (ITransaction) HttpContext.Current.Items[TRANSACTION_KEY];

}

else

{

return (ITransaction) CallContext.GetData(TRANSACTION_KEY);

}

}

set

{

if (IsInWebContext())

{

HttpContext.Current.Items[TRANSACTION_KEY] = value;

}

else

{

CallContext.SetData(TRANSACTION_KEY, value);

}

}

}

/// <summary>

/// If within a web context, this uses <see cref=”HttpContext” /> instead of the WinForms

/// specific <see cref=”CallContext” />. Discussion concerning this found at

/// http://forum.springframework.net/showthread.php?t=572.

/// </summary>

private ISession ThreadSession

{

get

{

if (IsInWebContext())

{

return (ISession) HttpContext.Current.Items[SESSION_KEY];

}

else

{

return (ISession) CallContext.GetData(SESSION_KEY);

}

}

set

{

if (IsInWebContext())

{

HttpContext.Current.Items[SESSION_KEY] = value;

}

else

{

CallContext.SetData(SESSION_KEY, value);

}

}

}

private static bool IsInWebContext()

{

return HttpContext.Current != null;

}

private const string TRANSACTION_KEY = “CONTEXT_TRANSACTION”;

private const string SESSION_KEY = “CONTEXT_SESSION”;

}

}

  • Have a look at the last two properties to see how the CallContext is used. These maintain the transaction and session across a single request thread.
  • InitSessionFactory() requires that HBM_ASSEMBLY be defined in the config file.
  • Finally, be sure to call CommitTransaction(), RollbackTransaction() and/or CloseSession() at the end of the request to clear out the CallContext at the end of the thread request.

Again, I will show you how to put all these pieces together in an upcoming post.

Advertisements

Written by Nelson

April 2, 2007 at 3:36 am

Posted in NHibernate

2 Responses

Subscribe to comments with RSS.

  1. I’ve been reading Billy McCafferty’s article as well, and it is indeed a very good article.
    The way of working using the ‘NHibernateSessionMgr’ and using the ‘Open Session in View’ pattern is perfectly usable in an ASP.NET application, but imho it is not a good solution in a WinForms app.
    IMHO, it’s a bit problematic to use this way of working in a WinForms app, since it is impossible to have 2 UoW’s (in this case open ISession’s) at the same time.
    Consider this scenario:
    – You have a form that lists all customers. This form has an edit button. Clicking on the edit button opens a detail form where you can change the details of a Customer.
    – Now, if you open 2 different customer-detail forms, they’ll share the same ISession. How will you cope with that ? When you close the detail-form, you’ll probably want to save the changes, and close the ISession. But, what happens then with the other detail-form ?
    – Or, what if you have another form which lists all the Orders for example ? What if you open the Customer-List form and the Orders-List form at the same time ?

    IMHO, in a WinForms scenario, you’ll want one Unit Of Work for each instance of a form.

    Frederik Gheysels

    April 30, 2007 at 8:22 pm

  2. hello,
    our team develope project in data Access layer with nhibernate ,if would you please describe for me how could use this code in dataAccess layer in windows application,
    if we use this code session automaticaly close,and we do not need control close session;

    zahrakarimi

    July 13, 2008 at 5:47 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: