Wrapping up nHibernate in repositories

In my last post I discussed wrapping up the two core objects of nHibernate, the sessionmanagerfactory and the session, in a helper class. At the end of the story I briefly mentioned how to use helper objects in a repository. In this post I am further exploring the actual database actions of the helper and how this helper can be used in real repositories in a “regular” DDD style. Again, don’t take this as the way to do things. The web is covered with a lot of ways to get that done and there are several tools available. This is just a minimalistic way which works well for me and does demonstrate some programming ideas. Feel free to comment.


The previous post discussed a way to manage nHibernate sessions. All data operations are performed on such a session. The major operations are described in this interface


public interface INhibernateHelper : IDisposable


{


    IQuery Query(string query);


    T UniqueResult<T>(IQuery query);


    IList<T> ListResult<T>(IQuery query);


    IEnumerable<T> EnumerateResult<T>(IQuery query);


    void BeginTransAction();


    void Commit();


    void Save(object dObject);


    void Delete(object o);


}


The main reason for describing the functionality as an interface lies in testing; an interface is relatively easy to mock.


The members which return data are defined as generic methods; this gives me the luxury of strongly typed data. At first sight it might be tempting to make the interface itself generic, that is:


public interface INhibernateHelper<T> : IDisposable


{


    T UniqueResult<T>(IQuery query);


}


But that will backfire when implementing the interface. To manage a shared sessionfactory I am using a static constructor. As every specific type instance of a generic class fires its own specific static constructor the sessionfactory can no longer be shared over different classes. Having only the methods themselves generic will do.


When it comes to writing to the database it is important to realize the way nHibernate works. Invoking Save or Delete will not immediately persist that data. It takes the session’s Flush method to trigger that. When flushing the data your objects will not always be persisted in the same order as the Save and Delete invocations. Things are further complicated because nHibernate will sometimes perform an implicit flush when querying to prevent returning stale data.


On a session you can start a transaction but you can only start one transaction on every session. In my implementation of transactions I’m very forgiving. BeginTransaction can be invoked again and again; the first invocation will start the nHibernate transaction. A Commit is always honored; in case no transaction was started the helper will just flush the data.


The main things when implementing all members is to take care of exceptions. A session cannot recover from an exception. In case you hit one the only solution is to close the session.


internal class NhibernateHelper : INhibernateHelper


{


    private static readonly ISessionFactory sessionFactory;


  


    static NhibernateHelper()


    {


        Configuration cfg = new Configuration();


        IDictionary props = new Hashtable();


        props.Add(“hibernate.dialect”, “NHibernate.Dialect.MsSql2000Dialect”);


        props.Add(“hibernate.connection.provider”, “NHibernate.Connection.DriverConnectionProvider”);


        props.Add(“hibernate.connection.connection_string”, nHibernateConnectionHelper.connectionString);


        cfg.SetProperties(props);


        cfg.AddAssembly(nHibernateConnectionHelper.domainAssembly);


        sessionFactory = cfg.BuildSessionFactory();


    }


  


    private static string connectionString


    {


        get


        {


            Type typeOfConnectionProvider = typeof (NHibernate.Connection.DriverConnectionProvider);


            PropertyInfo ConnectionStringPropertyInfo = typeOfConnectionProvider.GetProperty(“ConnectionString”,


                                                                                            BindingFlags.Instance | BindingFlags.NonPublic);


            return (string) ConnectionStringPropertyInfo.GetValue(sessionFactory.ConnectionProvider, null);


        }


    }


  


    private readonly ISession currentSession;


  


    internal NhibernateHelper()


    {


        if (string.IsNullOrEmpty(nHibernateConnectionHelper.connectionString))


            throw new Exception(“Sessionfactory needs a connectionstring”);


        if (nHibernateConnectionHelper.connectionString != connectionString)


            throw new Exception(string.Format(“Sessionfactory connection allready initialized as {0}”,connectionString));


        currentSession = sessionFactory.OpenSession();


    }


  


    private ITransaction tx = null;


  


    public void BeginTransAction()


    {


        if (tx == null)


            try


            {


                tx = currentSession.BeginTransaction();


            }


            catch (Exception)


            {


                currentSession.Close();


                throw;


            }


    }


  


    public void Commit()


    {


        if (tx == null)


        {


            try


            {


                currentSession.Flush();


            }


            catch(Exception)


            {


                currentSession.Close();


                throw;


            }


  


        }


        else


        {


            try


            {


                tx.Commit();


            }


            catch (Exception)


            {


                tx.Rollback();


                currentSession.Close();


            }


            finally


            {


                tx = null;


            }


        }


    }


  


    public IQuery Query(string query)


    {


        return currentSession.CreateQuery(query);


    }


  


    public void CloseSession()


    {


        currentSession.Close();


    }


  


    public void Save(object dObject)


    {


        try


        {


            currentSession.SaveOrUpdate(dObject);


        }


        catch (Exception)


        {


            currentSession.Close();


            throw;


        }


    }


  


  


    public void Delete(object o)


    {


        try


        {


            currentSession.Delete(o);


        }


        catch (Exception)


        {


            currentSession.Close();


            throw;


        }


    }


  


    public void Dispose()


    {


        currentSession.Dispose();


    }


  


    public T UniqueResult<T>(IQuery query)


    {


        try


        {


            return query.UniqueResult<T>();


        }


        catch (Exception)


        {


            currentSession.Close();


            throw;


        }


    }


  


    public System.Collections.Generic.IList<T> ListResult<T>(IQuery query)


    {


        try


        {


            return query.List<T>();


        }


        catch (Exception)


        {


            currentSession.Close();


            throw;


        }


    }


  


    public System.Collections.Generic.IEnumerable<T> EnumerateResult<T>(IQuery query)


    {


        try


        {


            return query.Enumerable<T>();


        }


        catch (Exception)


        {


            currentSession.Close();


            throw;


        }


    }


  


}


When hitting an exception all this helper does is clean up after which it rethrows the exception, it’s up to its user to take further action.


All database interaction through nHibernate is now stuffed in this helper class. It can be used by a generic repository.


public class Repository<T>


{


    private readonly INhibernateHelper hibernate;


    private readonly string className;


  


    public Repository(INhibernateHelper hibernate)


    {


        className = typeof(T).Name;


        this.hibernate = hibernate;


    }


  


    public IQuery BuildQuery(string queryString)


    {


        return hibernate.Query(string.Format(“from {0} where {1}”, className, queryString));


    }


  


    public IList<T> ListAll(string orderBy)


    {


        string queryString = string.Format(“from {0} order by {1}”, className, orderBy);


        IQuery query = hibernate.Query(queryString);


        return hibernate.ListResult<T>(query);


    }


  


    public IEnumerable<T> EnumerateAll()


    {


        string queryString = string.Format(“from {0}”, className);


        IQuery query = hibernate.Query(queryString);


        return hibernate.EnumerateResult<T>(query);


    }


  


  


    public IEnumerable<T> EnumerateInPeriod(string propertyName, DateTime from, DateTime till)


    {


        if (from == DateTime.MinValue)


            from = new DateTime(1753, 1, 1);


        if (till == DateTime.MaxValue)


            till = new DateTime(9999, 12, 31);


        string queryString = string.Format(“from {0} where {1} >= ? and  {1} < ?”, className, propertyName);


        IQuery query = hibernate.Query(queryString);


        query.SetDateTime(0, from);


        query.SetDateTime(1, till);


        return hibernate.EnumerateResult<T>(query);


    }


  


    public T FindUnique(string propertyName, string propertyValue)


    {


        string queryString = string.Format(“from {0} where {1}=?”, className, propertyName);


        IQuery query = hibernate.Query(queryString);


        query.SetString(0, propertyValue);


        return hibernate.UniqueResult<T>(query);


    }


  


    public T FindUnique(string propertyName, long propertyValue)


    {


        string queryString = string.Format(“from {0} where {1}=?”, className, propertyName);


        IQuery query = hibernate.Query(queryString);


        query.SetInt64(0, propertyValue);


        return hibernate.UniqueResult<T>(query);


    }


  


    public T FindUnique(IQuery query)


    {


        return hibernate.UniqueResult<T>(query);


    }


  


    public void Save(T domainObject)


    {


        hibernate.Save(domainObject);


    }


  


}


The repository receives the helper in its constructor. In a test scenario I can inject mocked persistence here. My repository has methods to retrieve and update data. Quite straightforward ones like ListAll and more fancy ones like EnumerateInPeriod. It’s up to you to fulfill your specific needs. To build the hibernate queries the name of the class is needed. In the constructor this name is pulled from the Type parameter.


Here’s an example how to use this repository. Given this domain class:


public class School : ValidatableDomainObject


{


    private int id;


    private string omschrijving;


  


    private static string noSchool = “Geen binding”;


  


    public virtual int Id


    {


        get { return id; }


        set { id = value; }


    }


  


    public virtual string Omschrijving


    {


        get { return omschrijving; }


        set { omschrijving = value; }


    }


  


    public static string NoSchool


    {


        get { return noSchool; }


        set { noSchool = value; }


    }


  


    protected override void DoValidate(RepositoryManager repositoryManager)


    {


        validateExpression(!string.IsNullOrEmpty(omschrijving), “Naam school ontbreekt”);


    }


  


}


A simple class with some properties and it does validation. A repository for this class will now look like:


public class SchoolRepository : Repository<School>


{


    public SchoolRepository(INhibernateHelper hibernate)


        : base(hibernate)


    {


    }


  


    public School OpAfkorting(string afkorting)


    {


        return FindUnique(“omschrijving”, afkorting);


    }


  


    public School GeenBinding()


    {


        return OpAfkorting(School.NoSchool);


    }


  


}


No big deal; there are two School specific members. The base Repository class does the hard work.


Now I can create a repository for every domain class in the application. To manage all of these the app needs a RepositoryManager. This class takes care of creating the hibernate helper and creating specific repositories on demand. This example is a part of such a manager; for the sake of demo it only includes two repositories. All others will follow exactly the same pattern.


public class RepositoryManager : IDisposable


{


    private static string dbConnection = “”;


    public static string DbConnection


    {


        get { return dbConnection; }


        set { dbConnection = value; }


    }


  


    private readonly INhibernateHelper nhHelper;


  


    private PersoneelRepository personeel;


    private SchoolRepository school;


  


    public RepositoryManager()


    {


        string assemblyName = Assembly.GetExecutingAssembly().FullName;


        nhHelper = NhibernateHelperFactory.CreateHelper(dbConnection, assemblyName);


    }


  


    public void BeginTransAction()


    {


        nhHelper.BeginTransAction();


    }


  


    public void Commit()


    {


        nhHelper.Commit();


    }


  


    public PersoneelRepository Personeel


    {


        get


        {


            if (personeel == null)


                personeel = new PersoneelRepository(nhHelper);


            return personeel;


        }


    }


  


    public SchoolRepository School


    {


        get


        {


            if (school == null)


                school = new SchoolRepository(nhHelper);


            return school;


        }


    }


  


    #region IDisposable Members


  


    public void Dispose()


    {


        nhHelper.Dispose();


    }


  


    #endregion


}


To store the database connection string there is a static member. On application setup it has to be initialized just once. The constructor uses the helperfactory discussed in the previous post. This manager is in the domain assembly which also contains the hibernate mappings themselves; the name of that assembly is needed for the nHibernate configuration and can be read using the GetExcutingAssembly method.


The repositories all share the same session wrapped up in the helper. Transactions are managed at this level. And so a transaction easily spans objects from several domain classes. The helper has to be disposed to clean up the session. The repository manager implements Idisposable and will clean up the helper in its implementation of Dispose.


Let’s see it in action


using(RepositoryManager repoManager = new RepositoryManager())


{


    repoManager.BeginTransAction();


    School mySchool = repoManager.School.OpAfkorting(“BADM”);


    IEnumerable<Personeel> persList = repoManager.Personeel.EnumerateAll();


    foreach (Personeel personeel in persList)


    {


        if (personeel.IsValid(repoManager) && (personeel.UitDienst))


        {


            personeel.VerbondenAanSchool = mySchool;


            repoManager.Personeel.Save(personeel);                       


        }


    }


    repoManager.Commit();


}


In one transaction I update all “personeel” which meets certain criteria. Note that I pass the repository-manager to the validation method. The validation can use it to do things like data look up.


Well that’s about it. I do hope this provided you with some insights on a way to use nHibernate in a DDD project.

This entry was posted in Coding, Data, Featured. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Pingback: | Blog | (Yet another) nHibernate sessionmanager for ASP.NET (MVC)

  • http://geekswithblogs.net/mhildreth/Default.aspx Mark Hildreth

    You start off..

    “In my last post I discussed wrapping up the two core objects of nHibernate, the sessionmanager and the session, in a helper class.”

    I’m assuming you meant SessionFactory rather than SessionManager?

    In any case, I’ve found that using the SessionManager from Castle works well for me, especially when you consider that I have a few computers using SQL Express, which requires a changed connection string. Thus, my nant build system can alleviate this problem by injecting the connection string into an XML file.

  • Paul Cowan

    I use Castle’s Nhibernate facility to wrap up my session management. It also has an HTTPModule that can be plugged in.

    I really like it.

    I’ve never seen a blog post that demonstrated it.

  • http://codebetter.com/blogs/peter.van.ooijen/ pvanooijen

    Would changing the nHibernateHelper class ‘s visibility to public make it more testable ? The Helperfactory, which returns an nHibernatehelper object, is public. As used in the promanagers’s constructor
    nhHelper = NhibernateHelperFactory.CreateHelper(dbConnection, assemblyName);

    I’ve loads of test on that. The main things I test there are the db, the mappings and hibernates interaction with it.

    An interface to the helper is used by the rpository part, this is mockable to test the repositories themseleves.

  • Doug

    Yet another example of perpetuating the cycle of untestable internal classes. ;) Otherwise, a very good example. There has been some work by Ayende and Bobby Diaz on NHibernate.Linq available in the rhino-tools project that is along these lines (also following the Unit of Work wrapper pattern).