CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Peter's Gekko

public Blog MyNotepad : Imho { }

March 2008 - Posts

  • Wrapping up nHibernate

    In a relative short span of time nHibernate has become a major member of my toolbox. It has become the way to work with a database, not only a small hap-snap apps but it's also making big strides into a constantly evolving system I'm working on. Setting up nHibernate several times has given me the opportunity to do some playing around. Browsing around you can find several ways to do it, some ways just focus on getting it done, some ways focus on performance, and some ways focus on resource management. Here I would like to discuss my way where I tried to take all aspects in account. Don't take it as the way; please do share your thoughts.

    <update>After a very good comment by rui I have corrected some of the code for the repository part. Thanks ! Blogging is just the best public code review you can get :)</update>

    Code will work with two very important nHibernate objects

    • The sessionfactory. This object swallows the configuration, from the mappings to the connectionstring to the database. Instantiating it is quite a job as it has to parse all mappings and check for corresponding domain classes. The good thing is that it will actively check all mappings; when the sessionfactory is successfully instantiated you know you're more than halfway. (Note that the sessionfactory does not check the database, in case there are errors in columns names of the mappings or in the connectionstring your code will not hit an exception until it actually tries to connect to the database). The sessionfactory is thread-safe all your code can work with one and the same sessionfactory. And as it is expensive to create the thing it pays off to create it only once.
    • The session. The sessionfactory provides you with session objects to work with the database. A session wraps up an actual connection to the database, so it holds an expensive unmanged resource. The mantra for "classical" ado.net code was always to open the connection as late as possible and to close immediately after firing your sql. More details on that here. The nHibernate sessionfactory provides session objects containing an open connection to the database. The session object has a method to close the connection, but there are several reasons not to do that. The most obvious one is lazy loading. For example when you load an invoice object from the database nHibernate will not load the data of the associated customer until your code actively touches the customer's properties. In case your code doesn't touch the customer the data will not be loaded. To load the data from the database nHibernate needs an open connection. In case you had already closed the session your code will hit a LazyLoadException. So a session object holds an open connection to the database, which requires extra care.

    I'm going to manage both the sessionfactory and the session in one nHibernateHelper class. The code needs only one sessionfactory, which has to be instantiated once. The sessionfactory is going to be a static (shared) member of the class. I took the idea to instantiate the sessionfactory in a static constructor from an nice article on theserverside. A static constructor fires the moment the class is loaded. That is the moment the class is first touched by running code. In the original code the configuration is loaded from a configuration file. My problem with that is that I want to be independent from that for reasons of testability and custom configuration. Most settings can be hard coded but something like the database connectionstring has to be set from code. A static constructor does not have any parameters and I can not set any members, the moment I touch the class the static constructor fires first and I'm too late. What does work is a small helper class.

    internal class nHibernateConnectionHelper

    {

        internal static string connectionString = "";

    }

      

    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.MsSql2005Dialect");

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

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

            cfg.SetProperties(props);

            cfg.AddAssembly("dlcr.domain");

            sessionFactory = cfg.BuildSessionFactory();

        }

     

    When the nHibernateConnectionHelperClass is touched first the static constructor of the NhibernateHelper class reads the updated connectionstring. It is used like this:

    public class NhibernateHelperFactory

    {

        public static INhibernateHelper CreateHelper(string dbConnection)

        {

            nHibernateConnectionHelper.connectionString = dbConnection;

            return new NhibernateHelper();

        }

    }

     

    Now the static constructor works as intended. This trick also works when both classes are in the same cs file. How the sessionfactory itself is set up is in a previous post.

    With the sessionfactory ready the nHibernatehelper can start its core business: managing a session. A new session is instantiated and opened in the instance constructor. This constructor performs some checks whether the sessionfactory will provide a session with the expected database. It reads the connectionstring from the sessionfactory property as described in the previous post.

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

    }

     

    As my nHibernateHelper objects wrap up an unmanaged resource (the database connection in the currentSession object) it has to implement IDisposable. The session object itself also implements IDisposable so the helper's implementation can be pretty straightforward.

    public void Dispose()

    {

        currentSession.Dispose();

    }

     

    The only thing exposed is the nHibernateHelperFactory which servers IHibernateHelper objects. My IHibernateHelper is pretty straightforward

    public interface INhibernateHelper : IDisposable

    {

        IQuery Query(string className);

        IQuery Query(string className, string propertyName, string propertyValue);

        IQuery Query(string className, string propertyName, long propertyValue);

        IQuery Query(string className, string whereClause);

        void Save(object domainObject);

        void Save(object[] domainObjects);

        void Delete(object domainObject);

    }

     

    It has methods to query, save or delete objects. These are the methods my repository needs to interact with the database. This is a sample repository

    public class Repository

    {

        private readonly INhibernateHelper nhh;

      

        public Repository(INhibernateHelper nHhibernateHelper)

        {

            nhh = nHhibernateHelper;

        }

      

        public IList<IBoeking> Boekingen()

        {

            return nhh.Query("Boeking").List<IBoeking>();

        }

      

        public IBTWpercentage BTWpercentage(string naam)

        {

            IQuery query = nhh.Query("BTWpercentage", "Omschrijving", naam);

            return query.UniqueResult<IBTWpercentage>();

        }

      

        public void Save(object o)

        {

            nhh.Save(o);

        }

      

    }

     

    The helper is injected in the repository's constructor. For testing purposes I could also inject a mock.

    The overall result is that by using a disposable helper I'm in full control of the connection to the database and I have all nHibernate's benefits like lazy loading.

    [Test]

    public void CanDemoHibernate()

    {

      

        using (INhibernateHelper nhl = NhibernateHelperFactory.CreateHelper("Data Source=.;Initial Catalog=DLCR;Integrated Security=True"))

        {

            Repository rps = new Repository(nhl)

            IBoeking bks = StubFactory.BoekingStub();

            IBTWpercentage bts = StubFactory.BTWstub();

      

            Boeking bk = new Boeking();

            bk.Omschrijving = bks.Omschrijving;

            bk.Totaal = bks.Totaal;

            bk.BTW = bts.Percentage;

            rps.Save(bk);

      

            // Lazy load ahead

            ISet<BoekingsRegel> regels = bk.Regels;

            foreach (BoekingsRegel regel in regels)

            {

                Console.Write(regel.DeDato);

                tab();

                Console.Write(regel.Links);

                tab();

                Console.WriteLine(regel.Rechts);

            }

            Assert.Greater(regels.Count, 0);

        }

    }

     

    By using the helper I will be sure the session is always properly closed, also when an exception is hit.

    Perhaps I'm somewhat over concerned when it comes to keeping control over open connections. Once I doubted ado.net's connection pooling only to find out that works very well. Also nHibernate does a good job of keeping the number of open connection's under control. When I open multiple repositories each using it's own nHibernateHelper object these will all use the same connection. My main point is that my helper gives me the feeling I'm still in control without sacrificing performance or functionality.

  • Setting and retrieving the database connectionstring in nHibernate

    All the good work nHibernate is doing may sometimes look like magic but just like any "old-style" db programming it needs a plain old connectionstring to get to the database. There are many ways to pass this connectionstring. In a lot of examples it is a part of the configuration file where it is either a part of the app.config or the web.config or even a custom xml file which is explicitly loaded from code.

    You can also completely configure nHibernate from code. This snippet does the essentials

    static public void LoadConfiguration(string domainAssembly, string connectionString)

    {

        dBconnectionString = connectionString;

        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", connectionString);

        cfg.SetProperties(props);

        cfg.AddAssembly(domainAssembly);

        sessionFactory = cfg.BuildSessionFactory();

    }

     

    Note that the database dialect is hard coded. The bare essentials, the name of the assembly providing the domain objects and the connectionstring, are passed in.

    So there are many way to specify the connectionstring. But what about the other way round? What if you want to know the connectionstring nHibernate is actually using? I've been Googling and browsing around on this and did find out I'm not the only one. I came up with a solution which works in some, but not all, scenarios. It is possible to read the connectionstring on an open session. Like this:

    public static string ConnectionString

    {

        get

        {

            ISession session = SessionFactory.OpenSession();

            string result;

            try

            {

                result = session.Connection.ConnectionString;

            }

            catch (ADOException nhaex)

            {

                result = nhaex.Message;

            }

            return result;

        }

    }

     

    But what if the session cannot be opened; most likely because the connectionstring is invalid? In such a case it would be nice to know that value. But here you will get a message telling you it cannot open the connection without containing any information what connection it cannot open.

    I have looked everywhere but could not find any readable trace of the desired string at all. As a workaround I now store the connectionstring when loading the configuration. But I'm not that satisfied with that as it looks redundant and is not idiot proof. So if anybody has an idea; you're more than welcome.

    Update:

    I’ve looked everywhere, but not with reflector or inside the nHibernate sourcecode. In a comment Mogens Heller Grabe provided the info that the connectionstring is a non public member of the SessionFactory. Using reflection you can retrieve the value of any private member, provided you know it’s name. Hereby I include his code, for the sake of clarity I’ve renamed the variables

    public static string ConnectionString

    {

        get

        {

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

            PropertyInfo ConnectionStringPropertyInfo = typeOfConnectionProvider.GetProperty("ConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);

            string connectionString = (string)ConnectionStringPropertyInfo.GetValue(sessionFactory.ConnectionProvider, null);

            return connectionString;

        }

    }

     

    Which works well; also without opening a connection. Imho it would be quite usefull to surface this property and make the getter public. I could even do it myself as nHibernate is OS. But for now this is good enough for me. Thanks !

     

More Posts