NHibernate – Repository pattern

I know that there allready are implementations for NHibernate-based repositories (eg. RhinoTools) but I can’t help to fiddling a bit with a design that I feel comfortable with. When building business applications I like to use a domain model (Read more – Martin Fowler) and to use repositories (Read more – Martin Fowler) for storing my domain objects. When designing we storage-layer (data-access-layer) I always define each repository using an Interface. This is something I do to be able to create fakes and mocks of my repositories; as well as create different implementations of them, e.g. one using NHibernate and another using SubSonic etc. Each implementation has it’s base-classes etc. just to get rid of repetitive boilerplate coding. For my NHibernate base implementation it looks something like this:

NHibContext (see earlier post) is responsible for creating NHibSessions (which is a wrapper around an NHibernate.ISession). The NHibContext is injected in my NHibRepository so that it can create a NHibSession for use within the repository (this is probably something that might get changed and instead a NHibSession will be injected directly). Than I create e.g. a NHibUserAccountRepository that extends the NHibRepository and also implements the IUserAccountRepository. Lets pick it apart.

Why IRepository?
I want a common repository interface independent of which underlying storage framework is used or what domain object I’m dealing with.

public interface IRepository<T> where T : class, IEntity
{
    void Commit();
    void Rollback();

    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);
}

Why IUserAccountRepository?
As I said before, I want to be able to create fakes and mocks of my repositories and I want to be able to use polymorphism when dealing with my domain-object-specific repositories, so that I can work against an interface, independent of the actual implementation.

public interface IUserAccountRepository : IRepository<UserAccount>
{
}

Why NHibRepository
I want a strongly typed repository-base that keeps me from writing boilerplate code in all my specific NHibernate-based repositories.

public abstract class NHibRepository<T> : IRepository<T>, IDisposable
    where T : class, IEntity
{
    protected NHibContext Context { get; private set; }
    protected NHibSession Session { get; private set; }

    protected NHibRepository(NHibContext context)
    {
        Context = context;
        Session = Context.CreateNewSession();
    }

    public void Dispose()
    {
        Disposer.TryDispose(Session);
    }

    public void Commit()
    {
        Session.Commit();
    }

    public void Rollback()
    {
        Session.Rollback();
    }

    public virtual void Insert(T item)
    {
        Session.Insert<T>(item);
    }

    public virtual void Update(T item)
    {
        Session.Update<T>(item);
    }

    public virtual void Delete(T item)
    {
        Session.Delete<T>(item);
    }

    public virtual T SingleBy(Expression<Func<T, bool>> query)
    {
        return Session.GetItemBy<T>(query);
    }

    public virtual IList<T> List()
    {
        return Session.GetList<T>();
    }

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

UserAccountRepository against NHibernate

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

Why NHibSession?
Simple. I want a slimmer and unified interface for interacting with the NHibernate-core and I want it to take care of transactions.

public class NHibSession
    : IDisposable
{
    private ISession InnerSession { get; set; }
    private ITransaction Transaction { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="NHibSession"/> class.
    /// </summary>
    /// <param name="session">The session.</param>
    public NHibSession(ISession session)
    {
        InnerSession = session;
        InnerSession.FlushMode = FlushMode.Commit;
        SetupNewTransaction();
    }

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

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (Transaction != null)
            {
                Transaction.Rollback();
                Transaction.Dispose();
                Transaction = null;
            }

            if (InnerSession != null)
            {
                InnerSession.Dispose(); //InnerSession.Close(); Do not use! It breaks when outer transactionscopes are active! Should only use Dispose.
                InnerSession = null;
            }                
        }
    }

    ~NHibSession()
    {
        Dispose(false);
    }

    public void Commit()
    {
        Transaction.Commit();
        SetupNewTransaction();
    }

    public void Rollback()
    {
        Transaction.Rollback();
        SetupNewTransaction();
    }

    private void SetupNewTransaction()
    {
        if (Transaction != null)
            Transaction.Dispose();

        Transaction = InnerSession.BeginTransaction();
    }

    /// <summary>
    /// Creates and returns a Query.
    /// </summary>
    /// <param name="queryString">The query string.</param>
    /// <returns></returns>
    public IQuery CreateQuery(string queryString)
    {
        return InnerSession.CreateQuery(queryString);
    }

    /// <summary>
    /// Creates and returns a Criteria.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public ICriteria CreateCriteria<T>()
        where T : class
    {
        return InnerSession.CreateCriteria<T>();
    }

    /// <summary>
    /// Gets an item that matches sent expression.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="query">The query.</param>
    /// <returns></returns>
    public T GetItemBy<T>(Expression<Func<T, bool>> query)
    {
        return InnerSession.Linq<T>().SingleOrDefault(query);
    }

    /// <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 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(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 GetListByCriterions<T>(null);
    }

    /// <summary>
    /// Returns a list of items matching sent expression.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="query">The query.</param>
    /// <returns></returns>
    public IList<T> GetListBy<T>(Expression<Func<T, bool>> query = null)
    {
        return InnerSession.Linq<T>().Where(query).ToList();
    }

    /// <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(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)
    {
        InnerSession.Delete(obj);
    }

    /// <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)
    {
        Delete(GetItemById<T, TId>(id));
    }

    /// <summary>
    /// Deletes by query.
    /// </summary>
    /// <param name="query"></param>
    public void DeleteByQuery(string query)
    {
        InnerSession.Delete(query);
    }

    /// <summary>
    /// Inserts sent item.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    public void Insert<T>(T obj)
    {
        InnerSession.Save(obj);
    }

    /// <summary>
    /// Updates sent item.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    public void Update<T>(T obj)
    {
        InnerSession.Update(obj);
    }

    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;
    }
}

Consume it
To show you how I consume it, let me show you a simple test.

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

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

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

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

Thats it. Hope you find it inspiring.

//Daniel