My fellow blogger here on the Degree Blog, Thor Halvor, had a blog post not too long ago titled “Don’t let the Configuration transform your Configs” where he recommends that people don’t transform their config files based on the solution configuration, and he points out a lot of things which is important to keep in mind. I, however, think that using the solution configurations to control which transformation files are applied to your config files works quite well.
As Thor Halvor points out in his blog post, you get a few extra config files when you create a web application in Visual Studio, namely Web.Debug.config, and Web.Release.config. These files are the XML transform files for Web.config, and when you build you application locally, they do exactly nothing (no matter if you have selected the debug or release solution configuration). If you use the “Build Deployment Package” or the “Publish” functionality in Visual Studio however, they are applied to your web.config file based on the currently selected solution configuration. Now this might work fine for a small personal project like your own homepage etc., but I doubt many people use this on large work projects. Another problem with this functionality (as Thor Halvor points out), is that it only works for Web.config, and in larger projects you often have more than one config-file (connection strings in a separate file, etc.).
So how can we use xml transform files for more than Web.config, and have the transformation be performed as part of an automated build on a build server? By adding a new target to the Web Application’s project file (.csproj) which will run as a part of the build (You need to right click on the project and choose “Edit project file”).
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="TransformConfigFiles" AfterTargets="AfterBuild" Condition="'$(TransformConfigFiles)'=='true'">
<DeleteAfterBuild Include="$(WebProjectOutputDir)\Web.*.config" />
<DeleteAfterBuild Include="$(WebProjectOutputDir)\ConnectionStrings.*.config" />
<TransformXml Source="Web.config" Transform="$(ProjectConfigTransformFileName)" Destination="$(WebProjectOutputDir)\Web.config" />
<TransformXml Source="ConnectionStrings.config" Transform="ConnectionStrings.$(Configuration).config" Destination="$(WebProjectOutputDir)\ConnectionStrings.config" />
<Delete Files="@(DeleteAfterBuild)" />
This new target will run after the “AfterBuild”-target, and it will transform both Web.config and ConnectionStrings.config, given that you specify the property “TransformConfigFiles” to be “true” (because we don’t want this be be done when we’re developing the application and building it in Visual Studio. As you can see above it uses the “Configuration”-property to decide which xml transform file to use (this property will contain the name of the selected solution configuration). After performing the transformation, it will clean-up/delete the xml transform files.
As you can see, I have created an additional config file, namely ConnectionStrings.config, as well as xml transform files for it (ConnectionStrings.Debug.config and ConnectionStrings.Release.config). The transformation files I’ve created aren’t group under the actual config file like Web.config’’s xml transformation files are, but if you wanted to you could edit the project file to do this.
You need to have xml transformation files for each solution configuration for those config files you want to transform, and they must all be named as <ConfigFileName>.<SolutionConfigurationName.config. I.e. if you create a new solution configuration called “Production” you would need to create a xml transform file called ConnectionString.Production.config (for Web.config you would just need to right click on it and select “Add Config Transforms”.
Inside my transformation files I’ve just put some simple transformations that will allow me to see that the config files are actually being transformed.
<connectionStrings configSource="ConnectionStrings.config" />
<add key="Configuration" value="" />
<add key="Configuration" value="Release" xdt:Transform="Replace" xdt:Locator="Match(key)" />
<add name="DefaultConnection" connectionString="Data Source=(local);Initial Catalog=dbMVC4;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="DefaultConnection" connectionString="Data Source=PreProdDatabaseServer;Initial Catalog=dbMVC4;Integrated Security=True" providerName="System.Data.SqlClient" xdt:Transform="Replace" xdt:Locator="Match(name)" />
In order to build the Web Application and have the config transformations take place we must run msbuild and specify the solution configuration we want to use, and that TransformConfigFiles should be true.
Running msbuild manually from the Visual Studio command prompt:
msbuild "C:\...\Testing.sln" /p:Configuration=Release /p:TransformConfigFiles=true /p:OutDir=C:\Temp\Testing_Release\
Under “C:\Temp\Testing_Release\_PublishedWebsites\MVC4” one should be able to find the Web Application with transformed config-files.
Now for the cool part, this approach will also work on your TFS build server, in your build definition just specify the build configuration and add TransformConfigFiles=true to the MSBuild Arguments.
On the project I’m currently working on, we have successfully been using this solution with TFS 2010 to automatically build and deploy our web application to the different testing environments for the better part of a year now!