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.

posted @ Sunday, June 08, 2008 4:35 PM


Print

Comments on this entry:

# re: Poor Man's Shards in NHibernate

Left by Ayende Rahien at 6/8/2008 10:17 PM
Gravatar

This would play hell with the second level cache.
You would have the same entity with the same id in several databases.
In most cases, the preferred approach would be to create several session factories.

# re: Poor Man's Shards in NHibernate

Left by mycall at 8/8/2008 5:58 PM
Gravatar

thanks! best example on how to implement a DriverConnectionProvider I found so far.

Your comment:



 (will not be displayed)


 
 
 
Please add 6 and 8 and type the answer here:
 

Live Comment Preview:

 
«August»
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456