tags:

views:

5595

answers:

9

can anyone provide/refer a proper OO type helper class for managing a singleton of the SessionFactory and then also for managing Sessions?

+1  A: 

Sure, this is what I used when I was getting started with NHibernate:

Session Factory

public class BaseDataAccess
{

protected ISession m_session;

public BaseDataAccess()
{
m_session=NHibernateHttpModule.CurrentSession;
}

public static ISession OpenSession()
{
Configuration config;
ISessionFactory factory;
ISession session;
config = new Configuration();

if (config==null)
{
throw new InvalidOperationException("NHibernate configuration is null.");
}

config.AddAssembly("My.Assembly.Here");
factory = config.BuildSessionFactory();

if (factory==null)
{
throw new InvalidOperationException("Call to Configuration.BuildSessionFactory() returned null.");
}

session = factory.OpenSession();

if (session==null)
{
throw new InvalidOperationException("Call to factory.OpenSession() returned null.");
}

return session;
    }
}

Let me know if that helps.

George Stocker
A: 

Thank you! This is helpful, but I'm looking for something that's a bit more robust.

For example, referenced on the NH site (http://hibernate.org/363.html#A9) is a contribution made by Andrew Mayorov and he posts three files such as this (http://nhibernate.sourceforge.net/contrib/Wiki_AndrewMayorovAspNet.zip).

Thing is that it doesn't come with much help. I'm not exactly sure how to make use of it properly. I tried some things, but ran into trouble.

Anyone have something which uses this or similar?

Donna
What do you mean by 'robust'? Can you be more explicit as to what is lacking? What did you try? And What trouble did you run into? I struggled when I first started with NHibernate, so I'd like to help if at all possible.
George Stocker
+11  A: 

Check out Billy McCafferty's work. His earlier version had some limitations, you'll need to correct the error handling around closing and flushing, and I'm not sure i have it right but I will post how I modified his stuff.

Billy is also working on a new framework that uses MVC & nHibernate called S#arp Architechure which I'm currently using, and so far so good.

Anyways here's my modified version of his code. I make no guanrtees on accuracy or completness and you are using this on your own risk. If you use this make sure you close the session out. If you have any questions drop me an email [joshua][dot][berke] at [gmail...you know the rest].

/// <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 const string DefaultConfigFile = "DefaultAppWeb.Config";
    private static readonly object _syncRoot = new object();
    #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.NHibernateSessionManager;
    }
}

/// <summary>
/// Private constructor to enforce singleton
/// </summary>
private NHibernateSessionManager() { }

/// <summary>
/// Assists with ensuring thread-safe, lazy singleton
/// </summary>
private class Nested
{
    static Nested() { }
    internal static readonly NHibernateSessionManager NHibernateSessionManager =
        new NHibernateSessionManager();
}

#endregion

/// <summary>
/// This method attempts to find a session factory stored in <see cref="sessionFactories" />
/// via its name; if it can't be found it creates a new one and adds it the hashtable.
/// </summary>
/// <param name="sessionFactoryConfigPath">Path location of the factory config</param>
private ISessionFactory GetSessionFactoryFor(string sessionFactoryConfigPath)
{
    Check.Require(!string.IsNullOrEmpty(sessionFactoryConfigPath),
        "sessionFactoryConfigPath may not be null nor empty");

    //  Attempt to retrieve a stored SessionFactory from the hashtable.
    ISessionFactory sessionFactory;// = (ISessionFactory)sessionFactories[sessionFactoryConfigPath];

    //  try and get a session factory if we don't find one create it
    lock (_syncRoot)
    {
        if (!sessionFactories.TryGetValue(sessionFactoryConfigPath, out sessionFactory))
        {
            Configuration cfg = new Configuration();
            if (sessionFactoryConfigPath != DefaultConfigFile)
            {
                Check.Require(File.Exists(sessionFactoryConfigPath),
                    "The config file at '" + sessionFactoryConfigPath + "' could not be found");
                cfg.Configure(sessionFactoryConfigPath);


            }
            else
            {
                cfg.Configure();
            }


            //  Now that we have our Configuration object, create a new SessionFactory
            sessionFactory = cfg.BuildSessionFactory();


            Check.Ensure(sessionFactory != null, "sessionFactory is null and was not built");
            sessionFactories.Add(sessionFactoryConfigPath, sessionFactory);
        }
    }



    return sessionFactory;
}

/// <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 RegisterInterceptorOn(string sessionFactoryConfigPath, IInterceptor interceptor)
{
    ISession session = (ISession)ContextSessions[sessionFactoryConfigPath];

    if (session != null && session.IsOpen)
    {
        throw new CacheException("You cannot register an interceptor once a session has already been opened");
    }

    GetSessionFrom(sessionFactoryConfigPath, interceptor);
}

public ISession GetSessionFrom(string sessionFactoryConfigPath)
{
    return GetSessionFrom(sessionFactoryConfigPath, null);
}
/// <summary>
/// Gets or creates an ISession using the web / app config file.
/// </summary>
/// <returns></returns>
public ISession GetSessionFrom()
{
    return GetSessionFrom(DefaultConfigFile, 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 GetSessionFrom(string sessionFactoryConfigPath, IInterceptor interceptor)
{
    var allSessions = ContextSessions;
    ISession session = null;
    if (!allSessions.TryGetValue(sessionFactoryConfigPath, out session))
    {
        if (interceptor != null)
        {
            session = GetSessionFactoryFor(sessionFactoryConfigPath).OpenSession(interceptor);
        }
        else
        {
            session = GetSessionFactoryFor(sessionFactoryConfigPath).OpenSession();
        }

        allSessions[sessionFactoryConfigPath] = session;
    }

    //session.FlushMode = FlushMode.Always;

    Check.Ensure(session != null, "session was null");

    return session;
}

/// <summary>
/// Flushes anything left in the session and closes the connection.
/// </summary>
public void CloseSessionOn(string sessionFactoryConfigPath)
{
    ISession session;
    if (ContextSessions.TryGetValue(sessionFactoryConfigPath, out session))
    {
        if (session.IsOpen)
        {
            session.Flush();
            session.Close();
        }
        ContextSessions.Remove(sessionFactoryConfigPath);

    }

}

public ITransaction BeginTransactionOn(string sessionFactoryConfigPath)
{
    ITransaction transaction;

    if (!ContextTransactions.TryGetValue(sessionFactoryConfigPath, out transaction))
    {
        transaction = GetSessionFrom(sessionFactoryConfigPath).BeginTransaction();
        ContextTransactions.Add(sessionFactoryConfigPath, transaction);
    }

    return transaction;
}

public void CommitTransactionOn(string sessionFactoryConfigPath)
{

    try
    {
        if (HasOpenTransactionOn(sessionFactoryConfigPath))
        {
            ITransaction transaction = (ITransaction)ContextTransactions[sessionFactoryConfigPath];

            transaction.Commit();
            ContextTransactions.Remove(sessionFactoryConfigPath);
        }
    }
    catch (HibernateException he)
    {
        try
        {
            RollbackTransactionOn(sessionFactoryConfigPath);
        }
        finally
        {
            throw he;
        }
    }
}

public bool HasOpenTransactionOn(string sessionFactoryConfigPath)
{
    ITransaction transaction;
    if (ContextTransactions.TryGetValue(sessionFactoryConfigPath, out transaction))
    {

        return !transaction.WasCommitted && !transaction.WasRolledBack;
    }
    return false;
}

public void RollbackTransactionOn(string sessionFactoryConfigPath)
{

    try
    {
        if (HasOpenTransactionOn(sessionFactoryConfigPath))
        {
            ITransaction transaction = (ITransaction)ContextTransactions[sessionFactoryConfigPath];

            transaction.Rollback();
        }

        ContextTransactions.Remove(sessionFactoryConfigPath);
    }
    finally
    {

        ForceCloseSessionOn(sessionFactoryConfigPath);
    }
}

/// <summary>
/// Since multiple databases may be in use, there may be one transaction per database 
/// persisted at any one time.  The easiest way to store them is via a hashtable
/// with the key being tied to session factory.  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 Dictionary<string, ITransaction> ContextTransactions
{
    get
    {
        if (IsInWebContext())
        {
            if (HttpContext.Current.Items[TRANSACTION_KEY] == null)
                HttpContext.Current.Items[TRANSACTION_KEY] = new Dictionary<string, ITransaction>();

            return (Dictionary<string, ITransaction>)HttpContext.Current.Items[TRANSACTION_KEY];
        }
        else
        {
            if (CallContext.GetData(TRANSACTION_KEY) == null)
                CallContext.SetData(TRANSACTION_KEY, new Dictionary<string, ITransaction>());

            return (Dictionary<string, ITransaction>)CallContext.GetData(TRANSACTION_KEY);
        }
    }
}

/// <summary>
/// Since multiple databases may be in use, there may be one session per database 
/// persisted at any one time.  The easiest way to store them is via a hashtable
/// with the key being tied to session factory.  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 Dictionary<string, ISession> ContextSessions
{
    get
    {
        if (IsInWebContext())
        {
            if (HttpContext.Current.Items[SESSION_KEY] == null)
                HttpContext.Current.Items[SESSION_KEY] = new Dictionary<string, ISession>();

            return (Dictionary<string, ISession>)HttpContext.Current.Items[SESSION_KEY];
        }
        else
        {
            if (CallContext.GetData(SESSION_KEY) == null)
                CallContext.SetData(SESSION_KEY, new Dictionary<string, ISession>());

            return (Dictionary<string, ISession>)CallContext.GetData(SESSION_KEY);
        }
    }
}

private bool IsInWebContext()
{
    return HttpContext.Current != null;
}

private Dictionary<string, ISessionFactory> sessionFactories = new Dictionary<string, ISessionFactory>();
private const string TRANSACTION_KEY = "CONTEXT_TRANSACTIONS";
private const string SESSION_KEY = "CONTEXT_SESSIONS";

public bool HasOpenTransactionOn()
{
    return HasOpenTransactionOn(DefaultConfigFile);
}

public void CommitTransactionOn()
{
    CommitTransactionOn(DefaultConfigFile);
}

public void CloseSessionOn()
{
    CloseSessionOn(DefaultConfigFile);
}

public void ForceCloseSessionOn()
{
    ForceCloseSessionOn(DefaultConfigFile);

}

public void ForceCloseSessionOn(string sessionFactoryConfigPath)
{
    ISession session;
    if (ContextSessions.TryGetValue(sessionFactoryConfigPath, out session))
    {
        if (session.IsOpen)
        {

            session.Close();
        }
        ContextSessions.Remove(sessionFactoryConfigPath);

    }
}

public void BeginTransactionOn()
{
    this.BeginTransactionOn(DefaultConfigFile);
}

public void RollbackTransactionOn()
{
    this.RollbackTransactionOn(DefaultConfigFile);
}

}

JoshBerke
+3  A: 

I recommend checking out Ayende's Rhino Commons library. The documentation is a bit sparce at the moment, but once you are up and running, working with NHibernate will be MUCH easier.

remotefacade
+3  A: 

I've had great success in the past using Spring.NET's NHibernate support modules. See http://www.springframework.net/downloads/Spring.Data.NHibernate/. You should be able to use the OpenSessionInView module and extend your DAOs off of the NHibernateSupport DAO to get full management support of the open session in view pattern.

Additionally, although I've never tried it, you should be able to use the above stated framework even if you opt out of the reset of Spring.NET's offerings (namely IoC and AOP).

Jason Whitehorn
+1  A: 

I have written a blog post on this where I use the managed web implementation of contextual session handling. Maybe that is what you are looking for?

Petter Wigle
A: 

NHibernate Burrow looks promising. I just started using it and i'm having no issues to date.

LordHits
A: 

Two suggestions:

  • Jeffrey Palermo's HybridSessionBuilder (jeffreypalermo.com/blog/use-this-nhibernate-wrapper-to-keep-your-repository-classes-simple)
  • See the code examples (specifically see Session 13) in the Summer of NHibernate (www.summerofnhibernate.com)
David Yates
A: 

You might like to consider making your DAL less concerned with managing NHibernate sessions by leveraging NHibernate.Burrow (or implementing a similar pattern yourself).

"NHibernate.Burrow is a light weight middleware developed to support .Net applications using NHibernate by providing advanced and smart session/transaction management and other facilitates."

If you decide to roll your own there are some useful links at the bottom of this page:

A useful google search term would be 'NHibernate Session Management' and 'Contextual Sessions'...

There's no shortage of ideas - you could say there are too many choices, hopefully opinion will start to gravitate around Burrow or something like it...

rohancragg