Monday, September 17, 2012

Loading module-specific connection strings in Orchard

At Alanta, we’ve been using Orchard as the platform for our corporate website, with mixed results. I like Orchard’s deep integration with ASP.NET MVC, and with the almost insane flexibility that it provides you as a developer. (Let’s here it for IOC!) But it’s also quite complex, with a pretty steep learning curve, and it still has some real maturing to do in terms of community support, such as themes and third-party modules and what-not. On top of that, it’s designed more as a self-contained CMS, rather than as a general-purpose framework. It assumes, in other words, that pretty much the only database you’ll ever want to talk to is its own; and the only way you’ll ever want to talk to that database is through its own (fairly well-designed) database access layer.

But that approach doesn’t work so well if you’re using Orchard to provide the content, navigation and theming scaffolding around an application that fundamentally needs to talk to a separate database – which of course is what we’re trying to get it to do. In doing this, we’re probably trying to insert a fairly square peg into a reasonably round hole, but it’s where we’re at, so we’ve had to come up with some creative solutions.

One of the first problems we ran into is how to get our custom Orchard module, which of course needs to talk to a separate database, to retrieve the connection string for that database. You can of course put the connection string into the web.config of the module in question and then try to load them the normal way:

_connectionString = ConfigurationManager.ConnectionStrings["AlantaEntities"].ConnectionString;

But that doesn’t work, because ConfigurationManager only reads the global web.config, not the web.config sitting in your module’s directory.

So we initially settled on a compromise, namely, adding the connection string to the global web.config. And that worked, but I wasn’t quite happy. Since we were using a source-code subscription of Orchard for our rollouts, it complicated the build process: we needed to maintain a web.config that was different from the Orchard default, and somehow merge that into our source code rollout. It wasn’t a huge compromise, but it had, if not code smell, at least build smell.

Finally, when we started our switch to Azure, it became imperative to address the problem. I really wanted to be able to run the standard Orchard website install, without having to build it ourselves, with all the dependencies and complexities that introduced. (Among other things, well, we couldn’t get Orchard to run the normal way, as a “Hosted Service”. It kept giving us some weird message, completely unknown on the forums, about an Autofac DependencyResolutionException.) But if we wanted it to run as a website (one of Azure’s new features, still in beta), we didn’t have an easy way to control what goes into the global web.config.

So we did what we probably should have done in the first place: moved our connection strings back into the module’s web.config file. It’s a few extra lines of code to read the connection string, but not difficult once you know the trick:

// Read the connection string from the *local* web.config (not the root).
var fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = HttpContext.Current.Server.MapPath("~/Modules/Alanta.Web.Corp/web.config");
var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
_connectionString = configuration.ConnectionStrings.ConnectionStrings["AlantaEntities"].ConnectionString;