A few months ago, I was turned on to the Enterprise Library Configuration Tool, a new component introduced in Microsoft's Enterprise Library. David Hayden has a good post about how to use this tool. Patterns and Practices explains it here. It addresses this very issue, and provides a user interface to view your settings and apply environmental-specific overrides. I started using this tool, and found that I enjoyed using that UI because I could easily see all the values for a setting across my environments.
I was able to automate the merging of the delta files created by the configuration tool into my build process with a executable provided by EntLib, called MergeConfiguration. I updated my MSBuild project file, TFSBuild.proj, with a target to run this executable after my code was dropped.
The downfall to this method was a limitation I discovered in the Configuration Tool. The settings I was allowed to manage and override were limited to Enterprise Library configuration sections, connection strings, and application settings. I could not change values of settings in sections such as System.Web and System.Net. Hmmm. My honeymoon phase with this tool was officially over.
Another solution to this problem that I like is a solution that Scott Hanselman talks about here. It is pretty simple and here are the steps...
- Create a new configuration in Visual Studio for each of your environments, such as Staging, Production, etc. You can model each one after the existing Release configuration for starters.
- Create a separate configuration file (i.e. web.config) for each of your environments. So you would end up having web.config.staging, web.config.production, etc.
- Create a Pre-Build Event that swaps the environmental-specific web.config for the existing one based upon the configuration that is being built. You can get a version that does this in Scott's post.
So...I have been using a hybrid approach that addresses these issues for me...
- I create a separate configuration file for my different environments (web.config.staging, etc).
- I add a target to my MSBuild project file (TFSBuild.proj when using TFS) to copy the correct configuration file to the root of my application after it is built. This way, if I have to copy multiple files for multiple configurations, all of the statements are right here.
- (Optional) I modify the properties on the configuration files in the root of my projects to set their Build Action to "None". This means that they will not be included in the build. I like the settings in those files to be local development settings, and know that they will not be replicated to other environments. So what is deployed is either the correct environment-specific config copied in Step 2, or none at all.
There are definitely other ways to solve this problem. If you have other good solutions, I would love to hear about them....