Home > .net 2.0, .net 3.5, c#, Fluent NHibernate, NHibernate, SQL, Visual Studio 2008 > Using Multiple NHibernate Session Factories

Using Multiple NHibernate Session Factories

March 6, 2009

A few months ago, I rewrote a few of my base repositories to follow some of the archtecture practices of the S#arp Architecture project.  I liked how easy it was to setup NHibernate*, mark domain objects, and setup mappings and highly recommend checking out their project.  Unfortunately, the whole project was a bit too bulky for what I needed and much of the functionality already existed in existing framework libraries we use at the office.

* When I mention NHibernate, I’m using Fluent NHibernate, currently at build 366 with a bit of modification to allow Oracle data access.  I’ve also enhanced the S#arp Architecture project to take advantage of the latest feature changes in Fluent NHibernate.

One pitfall, however, that I recently ran into was needing to access NHibernate multiple session factories at the same time—such as to access PeopleSoft and our internal student system to pull in demographic information.  The S#arp web session storage, by default, uses a single key opening a separate session overwrites the prior.

I found an open Issue on the S#arp tracker (#12) for such a functionality and a bit of code from Russell Thatcher to allow multiple sessions; however, I could never get it fully working—so I set off to create my own.

Updating the WebSessionStorage

To address the issue of storing multiple sessions in the HttpContext’s session state, the keys must be uniquely named when created.  To do this, I replaced the previous constructor of WebSessionStorage(HttpApplication) and added an additional parameter to create WebSessionStorage(HttpApplication, string).

This appends a name to the session key used to store the NHibernate ISession.

private readonly string CurrentSessionKey = “nhibernate.”;

 

public WebSessionStorage(HttpApplication application, string factoryKey)

{

       application.EndRequest += Application_EndRequest;

       CurrentSessionKey += factoryKey;

}

Everything else in WebSessionStorage remains the same.

NOTE: This ensures that we can find the ISession within the ASP.NET context using WebSessionStorage and isn’t required if you’re managing the the ISession yourself or using another mechanism for storage.

Updating the NHibernateSession

Since we’re looking to support multiple, keyed sessions, the NHibernateSession’s private fields of SessionFactory and SessionStorage should be changed into keyed Dictionaries.

private static readonly Dictionary<string, ISessionFactory> SessionFactories =

       new Dictionary<string, ISessionFactory>();

 

private static readonly Dictionary<string, ISessionStorage> SessionStorages =

       new Dictionary<string, ISessionStorage>();

To use these new dictionaries, we replace the change the logic in the Init method to add the factory and storage to the dictionary, rather than assign to a single variable.

public static FluentConfiguration Init(

       this FluentConfiguration configuration,

       ISessionStorage storage,

       string factoryKey)

{

       Check.Require(storage != null,

              “The session storage was null, but must be provided.”);

       Check.Require(!string.IsNullOrEmpty(factoryKey),

               “A unique key for the session factory was null, but must be provided.”);

 

       try

       {

              if (!SessionFactories.ContainsKey(factoryKey))

             {

                     SessionFactories.Add(

                           factoryKey,

                           configuration.BuildSessionFactory());

                    

                     SessionStorages.Add(

                           factoryKey,

                           storage);

 

                     return configuration;

              }

       }

       catch (HibernateException ex)

       {

              throw new

                     HibernateException(

                           “Cannot create session factory.”, ex);

       }

 

       return configuration;

}

Our Init statement, when called, then looks like, passing along the factory key with the ISessionStorage.

configuration.Init(storage, “FactoryKeyNameGoesHere”);

The Current static public property also needs to address the new Dictionary; however, cannot as a property as it needs the parameter of the factory key.  I converted this into a method that accepts the factory key and returns an ISession based on what it finds.  If it cannot find an open sesson (is null) in the dictionary, it opens a new one, sets it in the dictionary, and returns it to the caller.

public static ISession Current(string factoryKey)

{

       var session =

              SessionStorages[factoryKey].Session ??

              SessionFactories[factoryKey].OpenSession();

 

       SessionStorages[factoryKey].Session = session;

      

       return session;

}

That’s it in the NHibernateSession.

Updating the Repository & Decorating Inheriting Repositories

The Repository base class provides functionality to the CRUD methods through a protected Session property.  This property needs to be updated to call the new Current(string) method.  However, since the Repository doesn’t know, we need to find a way to communicate the “key” that a repository uses to ensure the correct session is accessed (so, for example, that our PeopleSoft queries don’t try to hit our SAP session—that confuses it. *grin*)

Randall had a great solution for this—an attribute applied to repositories that simply provides a place to store the “name” of the session.

[AttributeUsage(AttributeTargets.Class)]

public class SessionFactoryAttribute : Attribute

{

       public readonly string FactoryKey;

 

       public SessionFactoryAttribute(string factoryKey)

       {

              FactoryKey = factoryKey;

       }

 

       public static string GetKeyFrom(object myClass)

       {

              var key = string.Empty;

 

              //Get member info via reflection

              var info = myClass.GetType();

 

              //Retrieve custom attributes

              var attributes =

                     info.GetCustomAttributes(

                           typeof (SessionFactoryAttribute),

                           true);

 

              //Check for attribute

              if (attributes.Length > 0)

              {

                     var att =

                           (SessionFactoryAttribute) attributes[0];

                     key = att.FactoryKey;

              }

             

              return key;

       }

}

Now we simply decorate the inheriting repositories with SessionFactory.  Notice how the key matches what we used in the Init statement earlier.

[SessionFactory(“FactoryKeyNameGoesHere”)]

public class CourseRepository :
       NHibernateRepository<Course>, ICourseRepository, IDisposable { }

For a bit better design, you can also create a constant called, perhaps, FactoryKey or {data}FactoryKey and use that for the attributes and Init statement.  Then, if it needs changed—change it in one place.

Inside our Configuration class (PSConfiguration, for example), we have:

public const string FactoryKey = “PSProd”;

and

configuration.Init(storage, PSConfiguration.FactoryKey);

and then decorating our Repository classes:

[SessionFactory(PSConfiguration.FactoryKey)]

Now, to take advantage of our new SessionFactory attribute, we modify the Session property on our Repository base class.

protected virtual ISession Session

{

       get

       {

              var factoryKey = SessionFactoryAttribute.GetKeyFrom(this);

              return NHibernateSession.Current(factoryKey);

       }

}

Now the interface implementations (Get, Find, etc) are fetching the correct Session.

In summary, I’ve enabled accessing multiple NHibernate Session Factories from the S#arp Architecture framework (or my small implementation of it) by…

  • Updating the WebSessionStorage to accept a factory key when the session is created.
  • Updating the NHibernateSession to store multiple Factories and Storages in dictionaries and then access these dictionaries when the “current” session is requested.
  • Adding an attribute to Repositories to “name” which factory a certain repository should be selecting out of and then providing that session when called.

So far, things are working like a champ—tweaking and refactoring from here.🙂

  1. April 8, 2009 at 4:39 pm

    This is a big time saver for me while upgrading S#arp Architecture to have this functionality…thanks David!

  1. No trackbacks yet.
Comments are closed.
%d bloggers like this: