NHibernate – HbmNHibContext & FluentNHibContext

In this post I’m going to share some parts of my architechture that I use when working with NHibernate. I’m using a Dependency Injection framework (e.g. Microsoft Unity) to create my repositories, services etc.

All my repositores that are implemented using NHibernate takes something I call a “NHibContext”. The NHibContext has one main responsibility: “Creating Unit of Works” that are used within my repositories. To create these Unit of works, the NHibContext needs a way to create ISession’s from an ISessionFactory. To begin with, I have created two implementations (see below): HbmNHibContext, FluentNHibContext. But first lets look at the base-class:

NHibContext

/// <summary>
/// A NHibernate-context object which operates on a single-database.
/// Offers functionality for creating UnitOfWorks for use in e.g
/// Repositories that uses NHibernate.
/// To create a custom Context implementation, inherit from this
/// class and ensure that SessionFactory is assigned in cTor.
/// </summary>
public abstract class NHibContext
{
    protected ISessionFactory SessionFactory { get; set; }

    /// <summary>
    /// Creates an new UnitOfWork.
    /// </summary>
    /// <returns></returns>
    public NHibUnitOfWork CreateUnitOfWork()
    {
        return new NHibUnitOfWork(SessionFactory.OpenSession());
    }
}

HbmNHibContext
Is setup using plain old fashioned HBM-files and uses Xml-configuration for the database etc.

FluentNHibContext
Uses, like the HbmNHibContext, Xml-configuration for the database, but instead of Hbm-files, it uses Fluent-NHibernate for defining the mapping.

/// <summary>
/// Uses Xml-configuration for setup-config and for mappings is Hbm-files used.
/// </summary>
public class HbmNHibContext : NHibContext
{
    /// <summary>
    /// Initializes a new instance of the <see cref="HbmNHibContext"/> class.
    /// Since you do not provide an assembly that contains the mappings,
    /// you have to provide these via configuration of mapping-resources.
    /// </summary>
    public HbmNHibContext()
        : this(null)
    {}

    /// <summary>
    /// Initializes a new instance of the <see cref="NHibContext"/> class.
    /// </summary>
    /// <param name="assemblyName">The Name of the Assembly containing the mappings.</param>
    public HbmNHibContext(string assemblyName)
    {
        SessionFactory = CreateSessionFactory(assemblyName);
    }

    /// <summary>
    /// Creates and returns a session factory.
    /// </summary>
    /// <param name="assemblyName">Optional Name of the assembly that contains the mappings.</param>
    /// <returns></returns>
    private ISessionFactory CreateSessionFactory(string assemblyName)
    {
        var cfg = new Configuration();
        cfg = cfg.Configure();

        if (assemblyName.IsNotNullOrEmpty())
            cfg.AddAssembly(assemblyName);

        return cfg.BuildSessionFactory();
    }
}
/// <summary>
/// Uses Xml-configuration for setup-config and for mappings is Fluent-NHibernate used.
/// </summary>
public class FluentNHibContext
    : NHibContext
{
    /// <summary>
    /// Initializes a new instance of the <see cref="FluentNHibContext"/> class.
    /// </summary>
    /// <param name="assemblyWithMappings">The assembly containing the Fluent mappings.</param>
    public FluentNHibContext(Assembly assemblyWithMappings)
    {
        SessionFactory = CreateSessionFactory(assemblyWithMappings);
    }

    /// <summary>
    /// Creates and returns a session factory.
    /// </summary>
    /// <param name="assemblyWithMappings">The assembly containing the Fluent mappings.</param>
    /// <returns></returns>
    private ISessionFactory CreateSessionFactory(Assembly assemblyWithMappings)
    {
        var cfg = new Configuration();
        cfg = cfg.Configure();

        return Fluently.Configure(cfg)
            .Mappings(m =>
                      m.FluentMappings.AddFromAssembly(assemblyWithMappings))
            .BuildSessionFactory();

        //return cfg.BuildSessionFactory();
    }
}

HBM-Mappings
My mappings are located in the assembly that contains the repositories and not in the assembly containing my entities. This since the mappings are for storage and not domain-logic, hence the entities that are mapped is located in a sepparate domain-assembly.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
    xmlns="urn:nhibernate-mapping-2.2"
    namespace="Sds.X.Domain" assembly="Sds.X"
    schema="dbo"
    default-access="property"
    default-lazy="true">
  
  <class name="UserAccount" table="UserAccounts">
    <id name="Id" type="Int32" unsaved-value="null">
      <generator class="identity" />
    </id>
    <property name="Username" type="String" not-null="true" unique-key="true" />
    <property name="Password" type="String" not-null="true" />
    <property name="Email" type="String" not-null="true" unique-key="true" />
  </class>
</hibernate-mapping>

Fluent Mappings
My fluent-mappings are located in the same assembly as the one that contains my HBM-mappings.

[Serializable]
public class UserAccountMapping
    : ClassMap<UserAccount>
{
    public UserAccountMapping()
    {
        Table("UserAccounts");

        Id(p => p.Id).GeneratedBy.Identity().Not.Nullable();

        Map(p => p.Username)
            .Not.Nullable()
            .Unique();
        Map(p => p.Email)
            .Not.Nullable()
            .Unique();
        Map(p => p.Password)
            .Not.Nullable();
    }
}

Repositories
My repositories needs an injected NHibContext (preferably from a dependency injection framework), since the repository want’s to get an Unit of work from the Context. All repositories are defined by an interface that is designed to handle aggregate-root-entities. For simplicity I have defined an base-interface that specifies common operations that should exist through all my repositories, so my specific repository-interfaces inherits from this base-interface. Wy I work with interfaces is for being able to mock my repositories when performing tests. I also want to be able to create repositories that uses other persistence-technologies, like SubSonic, Linq-to-SQL or something else.

public interface IRepository<T> where T : class
{
    ITransaction CreateTransaction();

    void Insert(T item);
    void Update(T item);
    void Delete(T item);

    T SingleBy(Expression<Func<T, bool>> query);

    IList<T> List();
    IList<T> ListBy(Expression<Func<T, bool>> query);
}

For centralizing common functionality amongst my repositories i have created a base-class for my NHibernate-based repositories.

public abstract class NHibRepository<T> : IRepository<T>
    where T : class, new()
{
    protected NHibContext Context { get; private set; }

    protected NHibRepository(NHibContext context)
    {
        Context = context;
    }

    public virtual ITransaction CreateTransaction()
    {
        return new Transaction();
    }

    public virtual void Insert(T item)
    {
        using(var uow = Context.CreateUnitOfWork())
        {
            uow.Insert<T>(item);
            uow.SaveChanges();
        }
    }

    public virtual void Update(T item)
    {
        using (var uow = Context.CreateUnitOfWork())
        {
            uow.Update<T>(item);
            uow.SaveChanges();
        }
    }

    public virtual void Delete(T item)
    {
        using (var uow = Context.CreateUnitOfWork())
        {
            uow.Delete<T>(item);
            uow.SaveChanges();
        }
    }

    public virtual T SingleBy(Expression<Func<T, bool>> query)
    {
        T result;

        using (var uow = Context.CreateUnitOfWork())
        {
            result = uow.GetItemBy<T>(query);
        }

        return result;
    }

    public virtual IList<T> List()
    {
        IList<T> result;

        using (var uow = Context.CreateUnitOfWork())
        {
            result = uow.GetList<T>();
        }

        return result;
    }
    public virtual IList<T> ListBy(Expression<Func<T, bool>> query)
    {
        IList<T> result;

        using (var uow = Context.CreateUnitOfWork())
        {
            result = uow.GetListBy<T>(query);
        }

        return result;
    }
}

Consume the infrastructure (core-lib)
So far we have mostly been dealing with basic infrastructure code that doesn’t change so much. Now lets look at how this is consumed in the “domain”. My repositories for my aggregates than looks like:

public class UserAccountRepository : NHibRepository<UserAccount>, IUserAccountRepository
{
    public UserAccountRepository(NHibContext context)
        : base(context)
    {}
}

Consuming the repository
The code shown below uses a simple factory method. This is just for brevity. Usually I get the instance from an ObjectSpace that uses a dependecy-injection framework to resolve the correct implementation.

[TestMethod]
public void CanInsertSingleUser()
{
    var newUserAccount = CreateUserAccount();

    IUserAccountRepository userRepository = CreateRepository();
    userRepository.Insert(newUserAccount);

    Assert.AreEqual(1, StorageTestHelper.RowCount(UserAccountsTableName));
}

private static IUserAccountRepository CreateRepository()
{
    return new UserAccountRepository(StorageTestHelper.NHibContext);
}

Thats it for now. I hope you have found it interesting.

//Daniel

3 thoughts on “NHibernate – HbmNHibContext & FluentNHibContext

  1. Pingback: NHibernate – Repository pattern « Daniel Wertheim

    • Hi,

      Don’t know why I didn’t provice ZIP’s with the complete code. Actually having a hard time finding it know, but:

      /// <summary>
      /// An Unit-of-work implementation against NHibernate.
      /// </summary>
      /// <remarks>
      /// If disposed, the injected session is also disposed.
      /// </remarks>
      public class NHibUnitOfWork
          : IDisposable
      {
          private readonly object _lock = new object();
              
          /// <summary>
          /// Indicates if there are any un-flushed changes.
          /// </summary>
          private bool HasChanges { get; set; }
      
          /// <summary>
          /// Original NHibernate session that is wrapped.
          /// </summary>
          private ISession InnerSession { get; set; }
      
          public NHibUnitOfWork(ISession session)
          {
              this.HasChanges = false;
              this.InnerSession = session;
              this.InnerSession.FlushMode = FlushMode.Never;
          }
      
          #region " Object lifetime, Disposing "
      
          public void Dispose()
          {
              Dispose(true);
              GC.SuppressFinalize(this);
          }
      
          private void Dispose(bool disposing)
          {
              if (disposing)
                  this.FreeManagedResources();
          }
      
          ~NHibUnitOfWork()
          {
              this.Dispose(false);
          }
      
          /// <summary>
          /// Frees managed resources (.Net, other items implementing IDisposable)
          /// </summary>
          private void FreeManagedResources()
          {
              if (this.InnerSession == null) return;
      
              this.InnerSession.Close();
              this.InnerSession.Dispose();
              this.InnerSession = null;
          }
      
          #endregion
      
          /// <summary>
          /// Creates and returns a Query.
          /// </summary>
          /// <param name="queryString">The query string.</param>
          /// <returns></returns>
          public IQuery CreateQuery(string queryString)
          {
              return this.InnerSession.CreateQuery(queryString);
          }
      
          /// <summary>
          /// Creates and returns a Criteria.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <returns></returns>
          public ICriteria CreateCriteria<T>()
              where T : class
          {
              return this.InnerSession.CreateCriteria<T>();
          }
      
          /// <summary>
          /// Returns item via Id.
          /// </summary>
          /// <typeparam name="TReturn"></typeparam>
          /// <typeparam name="TId"></typeparam>
          /// <param name="id"></param>
          /// <returns></returns>
          public TReturn GetItemById<TReturn, TId>(TId id)
          {
              return this.InnerSession.Get<TReturn>(id);
          }
      
          /// <summary>
          /// Returns item via NHibernate Criterions.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="criterions"></param>
          /// <returns></returns>
          public T GetItemByCriterions<T>(params ICriterion[] criterions)
          {
              return AddCriterions(this.InnerSession.CreateCriteria(typeof(T)), criterions).UniqueResult<T>();
          }
      
          /// <summary>
          /// Returns a list of ALL items.
          /// </summary>
          /// <remarks>ALL items are returned.</remarks>
          /// <typeparam name="T"></typeparam>
          /// <returns></returns>
          public IList<T> GetList<T>()
          {
              return this.GetListByCriterions<T>(null);
          }
      
          /// <summary>
          /// Returns list of item matching sent criterions.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="criterions"></param>
          /// <returns></returns>
          public IList<T> GetListByCriterions<T>(params ICriterion[] criterions)
          {
              ICriteria criteria = AddCriterions(this.InnerSession.CreateCriteria(typeof(T)), criterions);
              IList<T> result = criteria.List<T>();
      
              return result ?? new List<T>(0);
          }
      
          /// <summary>
          /// Deletes sent item.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="obj"></param>
          public void Delete<T>(T obj)
          {
              this.InnerSession.Delete(obj);
              this.HasChanges = true;
          }
      
          /// <summary>
          /// Deletes sent item by id.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <typeparam name="TId"></typeparam>
          /// <param name="id"></param>
          public void DeleteById<T, TId>(TId id)
          {
              this.Delete<T>(this.GetItemById<T, TId>(id));
              this.HasChanges = true;
          }
      
          /// <summary>
          /// Deletes by query.
          /// </summary>
          /// <param name="query"></param>
          public void DeleteByQuery(string query)
          {
              this.InnerSession.Delete(query);
              this.HasChanges = true;
          }
      
          /// <summary>
          /// Inserts sent item.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="obj"></param>
          public void Insert<T>(T obj)
          {
              this.InnerSession.Save(obj);
              this.HasChanges = true;
          }
      
          ///// <summary>
          ///// Saves (inserts or updates) the item.
          ///// </summary>
          ///// <typeparam name="T"></typeparam>
          ///// <param name="obj"></param>
          //public void SaveItem<T>(T obj)
          //{
          //    this.InnerSession.SaveOrUpdate(obj);
          //}
      
          /// <summary>
          /// Updates sent item.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="obj"></param>
          public void Update<T>(T obj)
          {
              this.InnerSession.Update(obj);
              this.HasChanges = true;
          }
      
          /// <summary>
          /// Clears the UnitOfWork from unsaved changes.
          /// </summary>
          public void Clear()
          {
              lock (this._lock)
              {
                  this.InnerSession.Clear();
                  this.HasChanges = false;
              }
          }
      
          /// <summary>
          /// Saves the changes.
          /// </summary>
          public void SaveChanges()
          {
              if (!this.HasChanges) return;
      
              lock (this._lock)
              {
                  this.InnerSession.Flush();
                  this.HasChanges = false;
              }
          }
      
          private static ICriteria AddCriterions(ICriteria criteria, ICriterion[] criterions)
          {
              if (criterions != null)
                  for (int c = 0; c < criterions.Length; c++)
                      criteria = criteria.Add(criterions[c]);
      
              return criteria;
          }
      
      }
      

      //Daniel

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s