Transforming Xml in a .NET project build

For a while now, ASP.NET has supported the manipulation of web.config files through transforms – a build step that tweaks the Xml in the file depending on the current build configuration. Although that’s only available for web.config files by default, it’s a useful thing to be able to do for other Xml files. For example, app.config files in test assemblies and client applications could usefully be transformed to point at different databases or services in the same way as a website is.

SlowCheetah is a Visual Studio extension that aims to fix this by adding support for any Xml file to be transformed. It adds some VS niceness by way of context menus, but what I don’t like is that it requires the extension to be installed in order to build the project (it puts some custom build files in %LOCALAPPDATA%\Microsoft\MSBuild\SlowCheetah\). Personally I prefer to keep the amount of installable prerequisites for building my code to a minimum, and if you’re happy to get your hands a little dirty there is a solution.

The “magic” of config transforms is provided by one of the standard Microsoft build assemblies, Microsoft.Web.Publishing.Tasks.dll. This isn’t used by default in a non-web project, but you can easily change that…

First, open your project file in a text editor (I currently favour Sublime Text, which is pretty and fast but a little pricey). At the bottom of the project file, just before the closing <Project> tag, include the following:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('app.$(Configuration).config')">
  <!-- Generate transformed app config in the intermediate directory -->
  <TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
  <!-- Force build process to use the transformed configuration file from now on. -->
  <ItemGroup>
    <AppConfigWithTargetPath Remove="app.config" />
    <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
      <TargetPath>$(TargetFileName).config</TargetPath>
    </AppConfigWithTargetPath>
  </ItemGroup>
</Target>

(Note that the example above works for the app.config file; you’ll need to modify filenames to make it work with other Xml files.)

Then, find in your project file the line <None Include=”App.config” /> and replace it with the following:

<ItemGroup>
  <Content Include="App.config" />
  <Content Include="App.Debug.config">
    <DependentUpon>App.config</DependentUpon>
  </Content>
  <Content Include="App.Release.config">
    <DependentUpon>App.config</DependentUpon>
  </Content>
</ItemGroup>

You’ll need to add an extra Content Include for each configuration you add – unfortunately with this method you don’t get the straightforward “add transforms” context menu.

After that it’s a case of creating the files in your project directory, and then you’re ready to go. It’s not as slick as SlowCheetah, but it does keep your code portable.

comments powered by Disqus