Earlier this year we built an ASP.NET web application that would be deployed into multiple client sites. Some of these sites use SQL Server while others use Oracle; this was definitely a prime candidate for using an ORM (Object Relational Mapping) framework. We decided to use NHibernate (instead of the Entity Framework) since this application was based on an existing application that already used NHibernate. Since we are a Microsoft shop, we built a large portion of the application with SQL Server and created the Oracle version near the end (once we felt the database schema was stable).

Unfortunately we were not able to use the same mapping files for both databases since there were a few differences — especially around primary keys. This created an interesting issue since we were following NHibernate's recommended way to load mapping files — set all of your mapping files as Embedded Resources and simply add the Business Entity assembly to the Configuration object. However, this approach no longer worked because we now have two mapping files for each Entity — one for SQL Server and one for Oracle.

Our temporary solution was to manually set the mapping files we didn't want included to None — if you've read my other posts, you'll know I didn't like this option... but we had to hand to project off to our client who would be putting the finishing touches on it before deploying it to all of their clients.

A couple of weeks ago I was giving our client a hand setting up TFS in their environment so they could get automated builds going on all of their projects, and the wanted to know if I had any ideas on how we could automate the process of building separate versions of the application — one for SQL Server and one for Oracle. I happily did some digging thinking we could easily solve this by adding a little logic into the MSBuild target BeforeCompile in the Entity project.

If it was only that easy... even though you can modify the EmbeddedResource ItemGroup using the new to 3.5 Remove attribute, some of the original information is still hanging around when the underlying engine calls the CSC target. I'm not 100% convinced this approach is a dead end, but spinning my wheels for a sufficient period of time, I decided to tackle this problem with a different approach — modify how we load the mapping files in the application.

The first thing you need to do is change the hbm.xml mapping files to no longer be embedded resources. Next, change the NHibernate config file to no longer set the assembly mapping; we put all of the NHibernate session settings in a config file, this can also be done programmatically. Finally, modify the code that creates the NHibernate SessionFactory (in our case, this is a custom Http Handler) to load the correct set of mapping files based on the dialect.

I've included a snippet below and stripped out error handling and used literals in an attempt to make it easier to read.

NHibernate.Cfg.Configuration config = new NHibernate.Cfg.Configuration();

// load the session factory settings; connection.provider, dialect, etc.

string mappingFilesPath;
switch (config.Properties["dialect"])
    case "NHibernate.Dialect.MsSql2000Dialect":
        mappingFilesPath = Path.Combine(HttpContext.Current.Server.MapPath, "SQL.NHibernateMapping");

    case "NHibernate.Dialect.Oracle9Dialect":
        mappingFilesPath = Path.Combine(HttpContext.Current.Server.MapPath, "Oracle.NHibernateMapping");

// add all of the appropriate mapping files to the NHiberate Configuration object
DirectoryInfo di = new DirectoryInfo(mappingFilesPath);
foreach (FileInfo mappingFile in di.GetFiles("*.hbm.xml"))

Now you can leverage a number of different techniques to create an automated build process that will create both versions of the application — one for SQL Server, the other for Oracle.