Skip to content

Instantly share code, notes, and snippets.

@tobinharris
Created May 7, 2009 22:18
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tobinharris/108433 to your computer and use it in GitHub Desktop.
Save tobinharris/108433 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using FluentNHibernate;
using FluentNHibernate.AutoMap;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Context;
using NHibernate.Event;
using Portal.Core.Application.DataAccess;
namespace Portal.Core.Infrastructure.DataAccess
{
/// <summary>
/// Simple NHibernate Unit Of Work class that will work in ASP.NET and Unit Tests.
///
/// Usage:
///
/// GLOBAL.ASCX.CS
///
/// void Application_Start(object sender, EventArgs e)
/// {
/// UoW.Configure("server=local;catalog=test;");
/// }
///
/// void Application_BeginRequest(object sender, EventArgs e)
/// {
/// UoW.Start();
/// }
///
/// void Application_EndRequest(object sender, EventArgs e)
/// {
/// if(UoW.IsStarted)
/// UoW.End();
/// }
///
/// </summary>
public static class UoW
{
#region Environment enum
/// <summary>
/// What environment do we want to run? Web or Other (suitable for unit tests, desktop clients or remoting)
/// </summary>
public enum Environment
{
Web,
Other,
Test
}
#endregion
/// <summary>
/// NHibernate session factory is expensive to create.
/// Therefore we keep it as a static variable so we only create it once.
/// It's thread safe, so static if fine for both web and desktop.
/// </summary>
private static ISessionFactory _nhibernateSessionFactory;
/// <summary>
/// We stash the configuration here just to be helpful, in case the app needs it.
/// This is optional really.
/// </summary>
public static Configuration NHibernateConfiguration { get; private set; }
/// <summary>
/// Get the session. You must Start the Unit of Work before calling this.
/// </summary>
public static ISession Session
{
get { return _nhibernateSessionFactory.GetCurrentSession(); }
}
public static bool IsStarted
{
get
{
if (_nhibernateSessionFactory == null) return false;
return CurrentSessionContext.HasBind(_nhibernateSessionFactory);
}
}
/// <summary>
/// Get the session factory. Can be accessed once has be created.
/// </summary>
public static ISessionFactory SessionFactory
{
get { return _nhibernateSessionFactory; }
}
/// <summary>
/// Constructor. We'd only create One UoW class for the entire application (one per ASP.NET App Pool)
/// </summary>
/// <param name="nhibernateConfiguration"></param>
public static void Configure(Configuration nhibernateConfiguration)
{
NHibernateConfiguration = nhibernateConfiguration;
_nhibernateSessionFactory = nhibernateConfiguration.BuildSessionFactory();
}
public static Configuration Configure(string connectionString, Environment env)
{
return Configure(connectionString, env, x => {});
}
/// <summary>
/// Simpler constructor. We'd only create One UoW class for the entire application (one per ASP.NET App Pool)
/// </summary>
/// <param name="connectionString"></param>
/// <param name="env">Where will this UoW be used? Web? </param>
public static Configuration Configure(string connectionString, Environment env, Action<Configuration> configurationAction)
{
NHibernateConfiguration = new Configuration();
//we will poke some additional properties into the config
var additionalProperties = new Dictionary<string, string>();
//the first is the session context. This allows NHibernate to take care of where it's being used.
//See NHibernate in Action page 327
if (env == Environment.Web)
{
additionalProperties.Add("current_session_context_class", typeof (WebSessionContext).FullName);
}
else
additionalProperties.Add("current_session_context_class", typeof(CallSessionContext).FullName);
additionalProperties.Add("use_proxy_validator", "false");
//additionalProperties.Add("query.substitutions", "true 1, false 0, True 1, False 0, yes 'Y', no 'N', bw_or ^, bw_and &, bw_not ~");
AutoPersistenceModel mappings = MyMappings.Create();
if (env == Environment.Test && string.IsNullOrEmpty(connectionString))
{
SQLiteConfiguration
.Standard
.InMemory()
.QuerySubstitutions("true 1, false 0, True 1, False 0, yes 'Y', no 'N', bw_or ^, bw_and &, bw_not ~")
//.UsingFile("test.db")
.ShowSql()
.ConfigureProperties(NHibernateConfiguration)
.AddProperties(additionalProperties)
.AddAutoMappings(mappings)
//.SetListener(ListenerType.FlushEntity, new CustomSaveEventListener())
;
// if(Directory.Exists("c:\\Mappings"))
// mappings.WriteMappingsTo("c:\\Mappings\\");
// else
// Console.Write("Not dumping mappings to file for preview because no c:\\Mappings folder.");
}
else
{
//use Fluent to fluently configure the database
//you'd have to change this if using MySQL for example.
MsSqlConfiguration
.MsSql2005
.ConnectionString(c => c.Is(connectionString))
.QuerySubstitutions("true 1, false 0, True 1, False 0, yes 'Y', no 'N', bw_or ^, bw_and &, bw_not ~")
.ShowSql()
.ConfigureProperties(NHibernateConfiguration)
.AddProperties(additionalProperties)
.AddAutoMappings(mappings)
.SetProperty("generate_statistics", "true")
.SetListener(ListenerType.FlushEntity, new CustomSaveEventListener())
;
}
NHibernateConfiguration.SetListener(ListenerType.Update, new CustomUpdateEventListener());
NHibernateConfiguration.SetListener(ListenerType.SaveUpdate, new CustomSaveOrUpdateEventListener());
configurationAction.Invoke(NHibernateConfiguration);
//set the static session factory (that will live for as long as the AppPool does
_nhibernateSessionFactory = NHibernateConfiguration.BuildSessionFactory();
return NHibernateConfiguration;
}
/// <summary>
/// Start a new unit of work.
/// </summary>
public static void Start()
{
if(_nhibernateSessionFactory == null) return;
//stash a new session where we can get at it at any time. NHibernate already
//has a CurrentSessionContext class for keeping sessions somewhere accessible.
CurrentSessionContext.Bind(_nhibernateSessionFactory.OpenSession());
//We don't want to push any work to the DB unless we explicitly call Commit.
Session.FlushMode = FlushMode.Commit;
//all our work should be in a transaction
Session.BeginTransaction();
}
/// <summary>
/// Send all work done in the UoW's transaction to the database. Also starts a new transaction
/// ready for any additional work you do.
/// You can call this method multiple times for during a single Unit of Work
/// if you want to send several updates separately.
/// </summary>
public static void Commit()
{
Session.Transaction.Commit();
Session.BeginTransaction();
}
/// <summary>
/// End the current unit of work.
/// </summary>
public static void End()
{
//We want to roll back any active transaction. If the user of this class wanted to commit
//any updates then he should have to have called UoW.Commit().
if (Session.Transaction != null && Session.Transaction.IsActive) Session.Transaction.Rollback();
//close the session
Session.Close();
//Session is disposable, and therefor it's good to call Dispose() once we're done with it.
Session.Dispose();
//unbind the session from the context as we won't be needing it.
CurrentSessionContext.Unbind(_nhibernateSessionFactory);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment