Web.config transforms

, posted: 29-Nov-2011 12:21

Most web applications (and probably all ASP.NET applications) will, as they go through the various stages of the development and deployment process, be required to run in a number of different environments with different characteristics.  A development shop will typically have separate development and user acceptance testing environments at the very least, and then of course there's the production environment.

Each of these environments will typically (at least hopefully) have its own version of the database (usually on a different server, especially for production), and other settings that logically need to vary from one environment to another (the most obvious being for instance whether or not "debug" is enabled). There may additionally be different mail settings, different web service endpoint settings and so on.

Managing these differences has been tedious and error-prone to do manually, and not exactly a piece of cake with automated systems either (I remember having to include post-build steps to copy and rename configuration files, for instance).

Fortunately, Visual Studio 2010 includes a feature which improves this situation considerably.

If you create a new project using one of the Web templates (such as ASP.NET Web Application or ASP.NET MVC 3 Application for example, but not Web Site) and glance at the Solution Explorer you'll notice an arrow next to the Web.config, indicating the presence of dependent files. If you expand this arrow you'll see the following:



If you now open Web.Release.config, you'll see something like this:



Note the unfamiliar xdt:Transform attribute on the element - this has the value "RemoveAttributes(debug)", and it does exactly what it says: it is an instruction to remove the debug attribute from the compilation element. Since we do not typically want out production code to run as debug, this makes sense for the release version of a config file.

By default, any values in the original Web.config file not explicitly removed, overridden or modified will carry through to the final output, so the only things you need to specify explicitly in the transformation files are those you want to add, remove or change.

If you look at the commented-out examples in the Web.Release.config file (and I suggest you do as a good place to start), you'll notice that the first example shows a section containing an entry for a "MyDB" connection string, and in addition to the xdt:Transform attribute we have already seen there is an xdt:Locator attribute, with the value "Match(name)", which as you can probably guess instructs that in selecting the element to modify, it should be matched according to the name attribute of the element.

A connectionStrings section may contain multiple connection strings, so to identify which one you want to apply a particular transformation to you use a Locator attribute (the same applies in other cases where there is potential ambiguity - note that you didn't need one on the element because there is only one.

The Web.config transform is applied when the application is published - one obvious way to try this out is to select "Build->Publish " from the Visual Studio menu, and then use the publish method of your choice - I find that publishing to the local file system is a good way to verify results quickly.

The Web.config transformation applied is the one corresponding to the currently selected build configuration - so for instance to apply the transformations specified in Web.Release.config you would publish with the build configuration set to Release. If you add additional build configurations (which if you use automated builds you probably will) you can generate additional configuration transform files, which will be named to reflect the new or additional build configurations.

If for instance you use the Configuration Manager to create a new build configuration named "Test", you will find that when you right-click Web.config the Add Config Transforms context menu entry is now enabled. If you click this option it will generate a Web.Test.config file (as well as config transform files for any other build configurations you may have created).

Note that if for example you chose to base your hypothetical Test build configuration on Release (which is what I generally do) it will generate a Web.Test.config file that is a duplicate of the default (unmodified)  Web.Release.config file.

If we go back to our Web.Release.config file, remove the comments around the connectionStrings section and publish with build configuration set to Release, you may possibly be surprised by the result. The output Web.config file includes an ApplicationServices connection string because it is included in the project's Web.config file, but it does not include the MyDB entry that we added in Web.Release.config - this is because the xdt:Locator attribute is set to "Match(name)", and there is no existing connection string with a name attribute of "MyDB", so the match fails.

We can fix this by adding a "MyDB" connection string in the project's Web.config and leaving its connectionString attribute as an empty string. This is a good approach when an element such as a connection string is present in all build configurations but we want to give it different values in each (or for given subsets). If on the other hand you wanted to add a completely new connection string in one configuration (unlikely in the case of connection strings, but there are other elements where it would make sense) you would omit the xdt:Locator attribute and give xdt:Transform the value "Insert".

So what else can you do with xdt:Transform?

Possible values are as follows: Replace (replaces the matching element, or the first of a series if there is more than one match), Insert (inserts a new element at the end of the selected collection), InsertBefore (followed in brackets by an absolute XPath expression used to identify the element before which it is inserted), InsertAfter (similar to InsertBefore except for the position at which it inserts), Remove (removes the selected element or the first of a matching),  RemoveAll (removes the selected element or elements), RemoveAttributes (removes the specified attribute or a comma-delimited list of attributes - this is the transform that is enabled by default for the element's debug attribute in Web.Release.config) and SetAttributes (allows you to change selected attributes to the values they have on element containing the SetAttributes attribute, without replacing the elements containing those attributes).

More on xdt:Locator

The examples of matching I've shown have been simple matches on the value of an attribute, but the Locator syntax is actually much more flexible than that. "Match" can take not just a single attribute but a comma-delimited list, and for more complex matches you can instead use "Condition" or "XPath": both take an XPath expression as their argument, but whereas with "Condition" the expression evaluates a path relative to the selected element, with "XPath" it is absolute and applies to the entire configuration.

MSDN documentation covering Transform and Locator syntax (with examples) can be found here.

Publishing with Automated Builds

While it's all very well being able to publish a web application from your PC and have transforms applied as part of that process, in the real world (at least the nice part of the real world, where we all wish we lived) the finished web application will be built from source control by an automated build process and then deployed, perhaps as the final step in that process.

Microsoft are very fond of people using deployment packages to deploy web applications,  and their online documentation covers using web config transforms with deployment packages in some detail (this is a good place to start). If that's what you're using you should be fine (if there are any issues with the build automation, it's worth checking out this thread on stackoverflow.com).

However, if your build process simply builds the web application for XCopy deployment (which I'm certain is a very common scenario) you may be wondering how to cause the transformation to be applied, since in this case the build is not performing a "Publish" as such. Fortunately, you can handle this situation fairly easily by adding an AfterBuild step to your project file that executes TransformXml and then following the transform cleans up by deleting all every file from the output that matches the pattern "Web.*.config" (therefore leaving your output Web.config file and deleting the transform files, which you are unlikely to want to deploy to the web site). I encountered this issue myself and resolved it as described a little over a year ago, so you can find the solution described in some detail on my blog.

Download a trial of Visual Studio 2010.

About the author

Kevin Daly has been writing code professionally since 1986 and for kicks since about 1981. He has been using .NET and C# continuously since .NET 1.0 was still in beta (2001 For Those Who Came In Late). His programming interests span desktop, web and mobile development, but he is particularly interested in the XAML-based (or at least XAML-aware) technologies such as WPF, Silverlight and WinRT. He is also interested in creating legions of killer robots (although that one's been slow getting off the ground). Other examples of his views, rants and even the odd code sample can be found on his blog at http://kjdaly.com. He will now stop talking about himself in the third person because it's frankly a bit weird.

Other related posts:
Secure Development Tips and Techniques for use in Asp.net websites and web applications
Consuming JSON web APIs with Visual Studio






Add a comment

Please note: comments that are inappropriate or promotional in nature will be deleted. E-mail addresses are not displayed, but you must enter a valid e-mail address to confirm your comments.

Are you a registered Geekzone user? Login to have the fields below automatically filled in for you and to enable links in comments. If you have (or qualify to have) a Geekzone Blog then your comment will be automatically confirmed and shown in this blog post.

Your name:

Your e-mail:

Your webpage:

About the Visual Studio blog

In the years since the hugely successful release of Visual Studio 2005, Microsoft has used developer feedback from all over the world to introduce new features in later releases.

This sponsored blog will bring Visual Studio tips and tricks from well known developers in the New Zealand tech community directly to you.

Every second day during November and December 2011 you will find something new here. Make sure you bookmark this blog or subscribe to its RSS feed.    

Join us on Twitter

Find us on Facebook

Tags

Blog...
Database...
Techniques...
Testing...
Tools...
User Interface...
Windows Phone...


Other blog posts

Winners of Windows Phone compe...
Customise your Visual Studio 2...
Secure Development Tips and Te...
NuGet Package Manager extensio...
Competition: be in to win one ...
Consuming JSON web APIs with V...
Recommended documentation add-...
Test driven development in Vis...
Getting Started with Visual St...
Welcome to the Visual Studio b...


Recent comments

kiwiandy on Winners of Windows Phone competition announced, mo: mm the first one I try... ASB.. isnt there!?...

chiefie on Winners of Windows Phone competition announced, mo: Hmm the ASB Bank app is MIA....

Daniel Ballinger on Recommended documentation add-on for Visual Studio: Personally I don't find much value from a tool like GhostDoc if the generated do...



Disclaimer

The Visual Studio blog is sponsored by Microsoft NZ. The blog posts are the authors' genuine accounts of their experiences with Visual Studio, Windows Phone SDK and Windows Azure Platform and are not influenced or filtered by Microsoft New Zealand in any way.