Poor Man's Shards in NHibernate

Another loosely coupled post on Going to Production with MonoRail.

One thing I haven't stressed in recent posts is the absolute necessity to thoroughly test and understand the implications of your modifications when you start pushing a framework outside of the box.  In my post on Advanced Dependency Injection with Castle Windsor I lightly touched on the lifestyle implications of my patch to the code.  The same warning covers today's post when we start jimmy jacking with a custom connection provider in NHibernate.

If you've been following along you know that for various reasons we host multiple clients on a single instance of our B2B web application.  For other various reasons we like to keep the data for each client segregated into separate databases.  We use NHibernate for persistence ignorance so that seemed to be the logical place to start when attempting to segregate client data.  When I was doing my research it became apparent that at a high level I was trying to implement a horizontally partitioned database.  In my case, partitioned by client.  Hibernate supports horizontal database partitioning through ShardsDario Quintana is in the process of porting Shards to NHibernate, but I don't believe it is quite ripe yet.  Shards appears to address all of the nuances and edge cases that would reveal themselves when attempting to logically split data.  My needs were much cruder and I was willing to sacrifice a fully functional implementation (see first paragraph above though).  

Each client hosted in our application has their own configuration file.  Within this file is a database connection string that points to the database for the particular client.  As long as the database structure of each database is identical, NHibernate is cool with that.  What I ended up implementing is a custom IConnectionProvider that would load the current client's configuration, read the connection string, and create a connection using that, rather than the connection string that would normally be read from your hibernate.cfg.xml file.  The sample code gets the job done.

namespace My.Application
{
  public class ClientConfigurationConnectionProvider : DriverConnectionProvider
  {
    public override IDbConnection GetConnection()
    {
      string clientId = ClientIdProvider.Get();

      if (null == clientId)
      {
        return base.GetConnection();
      }

      ClientConfiguration clientConfiguration = ClientConfigurationManager.GetByClientId(clientId);
      string dbConnectionString = clientConfiguration.DbConnectionString;
      if (string.IsNullOrEmpty(dbConnectionString))
      {
        return base.GetConnection();
      }

      IDbConnection conn = Driver.CreateConnection();
      conn.ConnectionString = dbConnectionString;
      conn.Open();
      return conn;
    }
  }
}

Very straight forward.  By inheriting from the built in DriverConnectionProvider, most of the hard work is done for us.  To tell NHibernate to use our connection provider, a small change to the configuration file is required:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <!-- <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property> -->
    <property name="connection.provider">My.Application.ClientConfigurationConnectionProvider, My.Application</property>
    <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="connection.connection_string">Server=(local);Database=MyApplication;Trusted_Connection=True;</property>
    <property name="show_sql">true</property>
    <mapping assembly="My.Application" />
  </session-factory>
</hibernate-configuration>

Remember what I said though, depending on your database structure, and the features of NHibernate you choose to take advantage of, bad and unexpected things could happen with this implementation, so do your testing.

«June»
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345