API for Unit Of Work Pattern Implementation with NHibernate

Development API (code)
Table of Contents
Summary

Source code for the implementation of the namespace DesignPatterns.NHibernate.UnitOfWork.


Operations
IUnitOfWorkFactory.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.NHibernate.UnitOfWork
{
    public interface  IUnitOfWorkFactory
    {
        IUnitOfWork Create();
    }
}
IUnitOfWork.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.NHibernate.UnitOfWork
{
    public interface IUnitOfWork : IDisposable
    {
        void Commit();
        void Rollback();
    }
}
UnitOfWorkFactory.cs
Remarks

This implementation of the IUnitWorkFactory includes the parameteless overload of the constructor method ::InitSessionFactory(): This overload attempts to create the Nhibernate’s session factory and is to be used in the context of the multilayered application implementing the DDD methodololy. This is referred on the DDD section.

How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Training.NHibernate.Data;
using nhb = NHibernate;
using NHibernate.SqlCommand;
using System.Reflection;
using System.Collections;
using System.Configuration;
using NHibernate.Cfg;

namespace DesignPatterns.NHibernate.UnitOfWork
{
    public class NHSqlAuditor : nhb.EmptyInterceptor, nhb.IInterceptor
    {
        SqlString nhb.IInterceptor.OnPrepareStatement(SqlString sql)
        {
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.AUDIT, ErrorHandling.LogManager.LogCategoryType.SQL, Guid.Empty, null, System.Environment.NewLine + System.Environment.NewLine + sql.ToString() + System.Environment.NewLine);
            return sql;
        }
    }

    public class UnitOfWorkFactory : IUnitOfWorkFactory, IDisposable
    {
        private nhb.ISessionFactory _sessionFactory;

        #region "NHibernate automatic configuration process"
        private void InitSessionFactory()
        {
            nhb.Cfg.Configuration lvConfiguration = new nhb.Cfg.Configuration();

            //1. Read Nhibernate configuration file...

            string lvFile = ConfigurationManager.AppSettings["Nhibernate_ConfigurationPath"]
                                  + @"\conf\" + ConfigurationManager.AppSettings["ProjectID"]
                                  + ".cfg.xml";

            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Searching for Nhibernate configuration file: " + lvFile);
            if (!System.IO.File.Exists(lvFile))
            {
                // 1.1. No pre-configured file exists: Read NHibernate-section from app.config OR hbm file on current directory, if any
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Configuration file <" + lvFile + "> not found: Attempt to configure Nhibernate from default .hbm.xml file on current directoy or from application .config file...");
                lvConfiguration.Configure();
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "NHibernate configured successfully.");
            }
            else
            {
                // 1.1. Read pre-configured file...
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Configuration file found: Attempt to configure Nhibernate...");
                lvConfiguration.Configure(lvFile);
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "NHibernate configured successfully.");
            }

            //2. Determine elegible assemblies for importation...

            // 2.1. Fetch the calling assembly

            Assembly lvCallingAssembly = Assembly.GetCallingAssembly();

            ArrayList lvAssembliesForImportation = new ArrayList();

            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Search for elegible assemblies with names like <*.Infrastructure.Persistence.Entities>. Search start on called assembly " + lvCallingAssembly.FullName);

            // 2.2. Search elegible assemblies with names like "*.Infrastructure.Persistence.Entities"...
            foreach (AssemblyName lvCalledAssembly in lvCallingAssembly.GetReferencedAssemblies())
            {
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Searching on referenced assembly " + lvCalledAssembly.FullName);
                ImportReferencedAssemblies(lvCalledAssembly, ref lvAssembliesForImportation);
            }

            //3. Import mapping files as embbebed resources on selected assemblies...

            String lvStrImportedAssemblies = "";
            string lvStrImportingAssembly = "";

            if (lvAssembliesForImportation.Count == 0)
            {
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "No elegible assemblies for importation found: Added initial assembly " + lvCallingAssembly.FullName);
                lvAssembliesForImportation.Add(lvCallingAssembly.GetName().Name);
            }

            foreach (String lvAssembly in lvAssembliesForImportation)
            {
                try
                {
                    ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null,"Importing assembly " + lvStrImportingAssembly);
                    lvStrImportingAssembly = lvAssembly;
                    lvConfiguration.AddAssembly(lvAssembly);
                    ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Assembly " + lvStrImportingAssembly + " imported successfully.");

                    lvStrImportedAssemblies = lvStrImportedAssemblies + lvStrImportingAssembly + ",";

                }
                catch (Exception ex)
                {

                    ErrorHandling.LogManager.Log(
                                    ex, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null,
                                    string.Format("NHibernate error importing assembly: {0}\nImported successfully: {1}", lvStrImportingAssembly, lvStrImportedAssemblies.TrimEnd(new char[] { ',' })));
                    throw ex;
                }
            }

            //4. Build session factory...
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Attempt to create Nhibernate session factory...");
            this._sessionFactory = lvConfiguration.BuildSessionFactory();
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Nhibernate session factory created sucessfully.");

        }

        void ImportReferencedAssemblies(AssemblyName pAssemblyName, ref ArrayList pAssemblies)
        {

            if (pAssemblies.Contains(pAssemblyName.Name)) { return; }

            if (pAssemblyName.FullName.EndsWith("Infrastructure.Persistence.Entities"))
            {
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Found entities assembly with name: " + pAssemblyName.FullName);
                pAssemblies.Add(pAssemblyName.Name);
            }
            else if (pAssemblyName.FullName.EndsWith("Infrastructure.Persistence"))
            {
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Found persistence assembly with name: " + pAssemblyName.FullName + ". Load and search referenced assemblies...");
                Assembly lvBusinessAssembly = Assembly.Load(pAssemblyName);
                foreach (AssemblyName lvReferencedAssembly in lvBusinessAssembly.GetReferencedAssemblies())
                {
                    ImportReferencedAssemblies(lvReferencedAssembly, ref pAssemblies);
                }
            }

        }
        #endregion

        public UnitOfWorkFactory(nhb.ISessionFactory sessionFactory)
        {
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Create unit of work factory...");
            _sessionFactory = sessionFactory;
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Factory created.");
        }

        public UnitOfWorkFactory()
        {
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Create unit of work factory...");
            this.InitSessionFactory();
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Factory created.");
        }

        public IUnitOfWork Create()
        {
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Create new/open current session...");

            nhb.ISession session = _sessionFactory.OpenSession(new NHSqlAuditor());
            session.FlushMode = nhb.FlushMode.Auto;

            return new UnitOfWork(ref session);
        }

        #region "IDispose Implementation"
        ~UnitOfWorkFactory()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Dispose managed resources
            }

            // Dispose unmanaged resources

            if (_sessionFactory != null)
            {
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Dispose session factory...");

                if (!_sessionFactory.IsClosed)
                {
                    _sessionFactory.Close();
                    _sessionFactory.Dispose();
                }

                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Unit of work factory disposed.");
            }
        }
        #endregion

    }
}
UnitOfWork.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Training.NHibernate.Data;
using NHibernate;

namespace DesignPatterns.NHibernate.UnitOfWork
{
    public class UnitOfWork : IUnitOfWork
    {
        private string TYPE_OF_ORM = null;

        private ITransaction _transaction;
        public ISession Session { get; private set; }

        internal UnitOfWork(ref ISession session)
        {
            Session = session;
            Session.FlushMode = FlushMode.Auto;

            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Session " + session.GetHashCode().ToString() + " created [connection string: " + session.Connection.ConnectionString + "].");

        }

        public void BeginTransaction()
        {
            _transaction = Session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Started new SQL transaction " + Session.GetHashCode().ToString() + " [state: " + (_transaction.IsActive ? " opened" : " closed") + "].");

        }

        public void Commit()
        {
            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Commit transacion " + _transaction.GetHashCode().ToString() + "...");

            if (!_transaction.IsActive)
            {
                throw new InvalidOperationException("No transaction is active");
            }
            _transaction.Commit();

            ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Transacion " + _transaction.GetHashCode().ToString() + " committed.");

        }

        public void Rollback()
        {
            if (_transaction.IsActive)
            {
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Rollback transaction " + _transaction.GetHashCode().ToString() + "...");
                _transaction.Rollback();
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, null, "Transaction " + _transaction.GetHashCode().ToString() + " rolledback.");
            }
        }

        #region "IDispose Implementation"
        ~UnitOfWork()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Dispose managed resources
            }

            // Dispose unmanaged resources

            if (Session.IsOpen)
            {
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, TYPE_OF_ORM, "Close session " + Session.GetHashCode().ToString() + "...");
                Session.Close();
                ErrorHandling.LogManager.Log(ErrorHandling.LogManager.LogEntryType.INFORMATION, ErrorHandling.LogManager.LogCategoryType.ORM, Guid.Empty, TYPE_OF_ORM, "Session " + Session.GetHashCode().ToString() + " closed.");
            }
        }
        #endregion
    }
}
LogManager.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Reflection;

namespace ErrorHandling
{
    public class LogManager
    {
        public enum LogEntryType
        {
            ERROR =0,
            WARNING = 1,
            INFORMATION = 2,
            AUDIT = 3
        }

        public enum LogCategoryType
        {
            STARTCOMMAND =0,
            RUNNINGCOMMAND = 1,
            ENDCOMMAND = 2,
            ABORTEDCOMMAND = 3,
            OBJECTDUMP = 4,
            ORM =5,
            SQL = 6,
            UNCATEGORIZED = -1
        }

        private static StreamWriter _sw = null;

        public static StreamWriter LogChannel { get { return _sw; } }

        public static void Init(string pFilePath, string pFileName)
        {
            if (!Directory.Exists(pFilePath))
            {
                throw new  Exception("Invalid directory [" + pFilePath + "]: Unable to initialize log file.");
            }
            else
            {
                _sw =new StreamWriter(pFilePath + "\\" + pFileName);
                _sw.AutoFlush = true;
            }
        }

        public static void Close()
        {
            if (_sw != null) {
                _sw.Flush();
                _sw.Close();
            }
        }

        public static void Write(string pText)
        {
            _sw.WriteLine(pText);
        }

        public static void Log(LogEntryType pType, LogCategoryType pCategory, Guid pOperation, string pSubCategory, string pDescription)
        {
            Log(pType, pCategory, pOperation, pSubCategory, pDescription, null);
        }

        public static void Log(Exception pError, LogCategoryType pCategory, Guid pOperation, string pSubCategory, string pDescription )
        {
            lock (typeof(LogManager))
            {
                var _entry = DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss") + " " + "ERROR".PadRight(12,' ') + " " + pCategory.ToString().PadRight(14,' ') + " " + (pSubCategory == null ? "" : pSubCategory + " ") + (pOperation == Guid.Empty ? "" : "[" + pOperation.ToString() + "]") + ": " + pDescription;
                _entry = _entry + System.Environment.NewLine + System.Environment.NewLine +  pError.ToString();
                _sw.WriteLine(_entry);
                _sw.WriteLine();
            }
        }       

        public static void Log(LogEntryType pType, LogCategoryType pCategory, Guid pOperation, string pSubCategory, string pDescription, Object pObject )
        {
            lock (typeof(LogManager))
            {
                var _entry = DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss") + " " + pType.ToString().PadRight(12,' ') + " " + pCategory.ToString().PadRight(14,' ') + " " + (pSubCategory == null ? "" : pSubCategory + " ") + (pOperation == Guid.Empty ? "" : "[" + pOperation.ToString() + "]") + ": " + pDescription;
                _sw.WriteLine(_entry);

                if (pObject != null)
                {
                    _entry = DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss") + " " + "AUDIT".PadRight(12, ' ') + " " + "OBJECTDUMP".PadRight(14, ' ') + " :" + System.Environment.NewLine;
                    _sw.WriteLine(_entry);
                    ObjectDumper.Write(pObject, 0, _sw);
                    _sw.WriteLine();
                }
            }
        }

    }

    public class ObjectDumper
    {

        public static void Write(object element)
        {
            Write(element, 0);
        }

        public static void Write(object element, int depth)
        {
            Write(element, depth, Console.Out);
        }

        public static void Write(object element, int depth, TextWriter log)
        {
            ObjectDumper dumper = new ObjectDumper(depth);
            dumper.writer = log;
            dumper.WriteObject(null, element);
        }

        TextWriter writer;
        int pos;
        int level;
        int depth;

        private ObjectDumper(int depth)
        {
            this.depth = depth;
        }

        private void Write(string s)
        {
            if (s != null)
            {
                writer.Write(s);
                pos += s.Length;
            }
        }

        private void WriteIndent()
        {
            for (int i = 0; i < level; i++) writer.Write("  ");
        }

        private void WriteLine()
        {
            writer.WriteLine();
            pos = 0;
        }

        private void WriteTab()
        {
            Write("  ");
            while (pos % 8 != 0) Write(" ");
        }

        private void WriteObject(string prefix, object element)
        {
            if (element == null || element is ValueType || element is string)
            {
                WriteIndent();
                Write(prefix);
                WriteValue(element);
                WriteLine();
            }
            else
            {
                IEnumerable enumerableElement = element as IEnumerable;
                if (enumerableElement != null)
                {
                    foreach (object item in enumerableElement)
                    {
                        if (item is IEnumerable && !(item is string))
                        {
                            WriteIndent();
                            Write(prefix);
                            Write("...");
                            WriteLine();
                            if (level < depth)
                            {
                                level++;
                                WriteObject(prefix, item);
                                level--;
                            }
                        }
                        else
                        {
                            WriteObject(prefix, item);
                        }
                    }
                }
                else
                {
                    MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
                    WriteIndent();
                    Write(prefix);
                    bool propWritten = false;
                    foreach (MemberInfo m in members)
                    {
                        FieldInfo f = m as FieldInfo;
                        PropertyInfo p = m as PropertyInfo;
                        if (f != null || p != null)
                        {
                            if (propWritten)
                            {
                                WriteTab();
                            }
                            else
                            {
                                propWritten = true;
                            }
                            Write(m.Name);
                            Write("=");
                            Type t = f != null ? f.FieldType : p.PropertyType;
                            if (t.IsValueType || t == typeof(string))
                            {
                                WriteValue(f != null ? f.GetValue(element) : p.GetValue(element, null));
                            }
                            else
                            {
                                if (typeof(IEnumerable).IsAssignableFrom(t))
                                {
                                    Write("...");
                                }
                                else
                                {
                                    Write("{ }");
                                }
                            }
                        }
                    }
                    if (propWritten) WriteLine();
                    if (level < depth)
                    {
                        foreach (MemberInfo m in members)
                        {
                            FieldInfo f = m as FieldInfo;
                            PropertyInfo p = m as PropertyInfo;
                            if (f != null || p != null)
                            {
                                Type t = f != null ? f.FieldType : p.PropertyType;
                                if (!(t.IsValueType || t == typeof(string)))
                                {
                                    object value = f != null ? f.GetValue(element) : p.GetValue(element, null);
                                    if (value != null)
                                    {
                                        level++;
                                        WriteObject(m.Name + ": ", value);
                                        level--;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private void WriteValue(object o)
        {
            if (o == null)
            {
                Write("null");
            }
            else if (o is DateTime)
            {
                Write(((DateTime)o).ToShortDateString());
            }
            else if (o is ValueType || o is string)
            {
                Write(o.ToString());
            }
            else if (o is IEnumerable)
            {
                Write("...");
            }
            else
            {
                Write("{ }");
            }
        }
    }

}