If you’ve worked on the Microsoft ASP.NET platform for any length of time, in an environment where you had to manage deployment of your application across multiple servers, then you’ve probably stared at .config files until your head pounds. In the past we often managed this by keeping multiple copies of each .config and using pre-build events to copy the right one to the right place based on the active configuration. Recently my colleague Erez clued me in to the fact that Microsoft had stepped up and offered a solution in Visual Studio 2010 called “config transformations.”
Config transformations are essentially a set of rules, similar to an XSLT in intent, if not in syntax, that replace or transform the content of specific nodes in the .config XML when the site is published. The full capability is quite powerful, but I am going to ignore 90% of it here. If you want to understand the whole thing there is a useful post on the Visual Web Team Developer blog. It’s very interesting stuff, but my favorite line was the one that began, “Simplest Approach: If you do not mind replicating the entire web.config file in web.staging.config then you can certainly do so…” Bingo! I may take advantage of all the neato matching rules later. For now I’m happy if I can achieve what we do now without requiring script to be created and maintained in a pre-build event.
Fortunately this is dead simple to do. The first step will be to create the transforms. The idea is that you have a base configuration named web.config, and one or more transforms that are applied to it when publishing under specific configurations. If you’re starting with a new web app then transforms for the default “Debug” and “Release” configurations will be created for you. You will find them as file nodes under web.config in the Solution Explorer, similar to the way code behind and designer files are handled. If, like us, you have a solution that predates VS2010 and want to start using config transforms, here’s what you do:
First, make sure you have your configurations set up. We usually have local (Dev), Test, and Production configurations on web apps. Right-click the existing web.config file in Solution Explorer and click “Add Config Transforms.”
Visual Studio will add transforms for any existing configuration that doesn’t already have one. In this simple example the result is two new files: Web.Debug.config and Web.Release.config. Here’s what it should look like after you finish:
The next step is to delete one of them. Why? Well, recall that these are transforms. They change the base .config so that it will work in another environment. Which config doesn’t need to be transformed? The web.config that you use to run locally. What I like to do is modify the existing, or default Web.config so that it is set up to work in local debug situations, and then use transforms to modify it when the app runs in other environments. So in the example above you would get rid of Web.Debug.config, modify Web.config for local debugging, and modify the transform in Web.Release.config to work in the production environment. Here’s a simple Web.config that I will use to illustrate the rest of the example:
<?xml version="1.0"?> <configuration> <appSettings> <add key="baseUrl" value="http://localhost:32767" /> </appSettings> <connectionStrings> <add name="SiteDB" connectionString="server=localhost;database=SiteDB;uid=user;pwd=password"/> </connectionStrings> </configuration>
As you can see this is set up for local debugging. Now for the release config. If you open up one of the transform files in the editor you’ll notice that it just contains an empty configuration node, along with some additional namespace information. Since we’re taking the simple route and swapping the entire file, the next thing to do is copy the contents of the configuration node from Web.Config to Web.Release.Config and modify it to work in the release environment. We also have to add the attribute that tells the transform tool to swap out the entire configuration node. Here”s what the completed file looks like:
<?xml version="1.0"?> <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xdt:Transform="Replace"> <appSettings> <add key="baseUrl" value="http://www.mysite.com" /> </appSettings> <connectionStrings> <add name="SiteDB" connectionString="server=proddbserver;database=SiteDB;uid=user;pwd=password"/> </connectionStrings> </configuration>
Note the additional namespace attribute on the configuration node. That namespace contains the element and attribute definitions used to define the transformation rules. The template will add this for you when it creates the files. The second attribute on the configuration node is one we need to add manually. The attribute xdt:Transform=”replace” tells the tool to replace the entire contents of a node in the base config with the contents of the same node in the transform. Since we placed this rule on the root configuration node, the entire contents of the config will get pasted in when we publish.
And that’s pretty much it. Once you have this set up the transform will be applied when you publish the site under a named configuration for which a transform exists. One thing to note, which confused me for a couple of minutes, is that the transforms are not applied on a simple local build. If you want the transform to run without publishing you apparently have to add a custom task to your project file and then call it from an event. This decision doesn’t really make sense to me, but I haven’t taken the time to research it and see why they did it this way. Fortunately all my local builds are under my “Dev” configuration, which is handled by the base Web.config, so this doesn’t really cause me an issue. When I publish to test or production the transforms run, and everything works great.