Monthly Archives: August 2010

What i dislike about the Web.config Transformation in VS2010

There are a couple of things that i strongly dislike about the Web.config transformation in VS2010:

  • Only works with XML files (eg: Can’t be used to generate a release notes.txt file)
  • Does not seem to support externalized sections, eg: log4net.config in a separate file
  • No support to copy/paste transform files
  • Only works when Visual Studio 2010 is installed (And i am still not convinced a build server should have this).
  • Ties environment to build configuration

Lesson learned: Don’t trust your co-workers, always double-check!

  • Having multiple transformations is easy-peasy, just invoke the TransformXml task for all your config files and make sure your transformation files are correct. For log4net this would look like:
  • <log4net xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
     <root>
      <level value="ERROR" xdt:Transform="Replace"/>
     </root>
    </log4net>
    
  • The support for copy/paste can be achieved by removing the DependentUpon tag in your proj file (At the cost that you don’t have the + sign in solution explorer which ‘hides’ the transforms files)

Making the TemplateFileTask easier to use…

One of the disadvantages of the TemplateFile task (msbuildtasks) is the fact that it requires a lot of typing to define template values:

<ItemGroup Condition= " '$(ConfigurationEnvironment)'=='build' ">
 <Tokens Include="a">
  <ReplacementValue>localhost</ReplacementValue>
 </Tokens>
 <Tokens Include="b">
  <ReplacementValue><mynode/></ReplacementValue>
 </Tokens>
</ItemGroup>

Here is a format proposition to make this a lot more finger friendly:

<configuration>
 <variables env="build">
  <x name="a">localhost</x>
  <x name="b><mynode/></x>
 </variables>
</configuration>

Here is the msbuild script we need to achieve that:

<PropertyGroup>
 <ConfigurationFile>configuration.xml</ConfigurationFile>
 <ConfigurationEnvironment>build</ConfigurationEnvironment>
</PropertyGroup>

<!-- Retreive all template values for the specific environment -->
<XmlQuery XmlFileName="$(ConfigurationFile)" XPath = "//variables[@env='$(ConfigurationEnvironment)']/*">
 <Output TaskParameter="Values" ItemName="Values" />
</XmlQuery>

<!-- Construct @Tokens -->
<ItemGroup>
 <Tokens Include="%(Values.name)">
  <ReplacementValue>%(Values._innerxml)</ReplacementValue>
 </Tokens>
</ItemGroup>

<!-- Generate the configuration files -->
<Message Text="Available variables:" />
<Message Text="====================" />
<Message Text="%(Tokens.Identity): %(Tokens.ReplacementValue)" />

Happy coding!

Clean TemplateFile hack

A while ago i wrote about a Clever TemplateFile hack to use some xml block as ReplacementValue. Today i realized there is a clean way to achieve this by defining the value as CDATA:

<TemplateTokens Include="mex">
 <ReplacementValue>
  <![CDATA[<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />]]>
 </ReplacementValue>
</TemplateTokens>