Many of the web applications we create are deployed many different times, providing many different instances.  Each instance is slightly different, but only in terms of configuration: they use different databases, possibly different sitemaps, different themes, and different terminology (via resource files).  Aside from those things though, the applications are exactly the same.  We want to be able to refresh deployed systems quickly and easily without having to do a lot of manual file manipulation.  In this post, I’ll discuss how we addressed the problem for one specific source of configuration information: the web.config file.

If your custom configuration is just stored in a web.config file, why not just leave the old web.config file in place when you’re deploying changes?  That would be great, but the fact is that the web.config stores much more than instance-specific information.  It also stores a lot of "global" information that applies to all instances of the application.  What happens when you need to deploy changes that were made to that "global" information?  Previously, our solution was to painstakingly merge the changes in to each web.config file.  We probably could have automated this by creating and applying diffs, but that’s only a minor improvement.

A better solution is to refactor your web.config file using the ‘configSource’ and ‘file’ attributes.  Per the MSDN documetation, the ‘configSource’ attribute allows you to move the definition of a configuration section to an external file.  This attribute is inherited by all configuration section elements, so it works fine even with 3rd party configuration, like log4net.  Here’s an example web.config file before:

   1: <configuration>
   2:     ...
   3:     <connectionStrings>
   4:         <add name="SqlServer2005" 
   5:             connectionString="Data Source=.;Initial Catalog=Inst1;Trusted Connection=true"
   6:             providerName="System.Data.SqlClient"/>
   7:     </connectionStrings>
   8:     ...
   9: </configuration>

Notice how the web.config is referencing an instance-specific database?  Well, we can move that to an external file using the ‘configSource’ attribute:

   1: <configuration>
   2:     ...
   3:     <connectionStrings configSource="Custom\connectionStrings.config" />
   4:     ...
   5: </configuration>

For the ‘appSettings’ element, you can also use the ‘file’ attribute.  Unlike ‘configSource’, which requires that the element be exclusively defined in the external file, the ‘appSettings’ element in the web.config file can extend or override the settings defined in the external source reference by the ‘file’ attribute, like so:

   1: <configuration>
   2:     ...
   3:     <appSettings file="Custom\appSettings.config">
   4:         <add key="OverridenKey" value="NewValue" />
   5:     </appSettings>
   6:     ...
   7: </configuration>

By combining these techniques, we were able to remove all instance-specific code from our web.config files and consolidate everything into a single folder called "Custom".  The files in "Custom" contain only the things that vary from instance to instance, so all we have to do during an update is leave the existing "Custom" folder in place.  Much, much easier than managing things by hand!

There’s still more to do, like figuring out how to deal with sitemaps, resource files, etc, but I’m still working on that.  If I find anything cool, I’ll be sure to post about it.