The Magic of Config Transformations

Fog, trees, and a lightWe have all used the magic of the Web.config transformations.  This is when we have our Web.config and connected to that is a version that does some work for release and one for debug; Web.Release.config and Web.Debug.config.  These are a great way to automatically have the correct target use the correct settings, especially connection strings.  Whether we use that or not and just leave it as something that looks cool is a different thing.

Did you know you can get this magic for your console applications as well?  I know, a web developer doing console apps?  As an enterprise one sometimes I find it is the best solution to some problems.  While it seems like a small thing I wished I had it in the console applications, I am happy to say that I have found it!

What is a Config Transformation?WebConfigs

Just in case you are wondering what I am talking about please allow me to explain a bit.  In your web project you should see something like the image to the right of this text.  You have a Web.config file, and under it is a Web.Debug.config and Web.Release.config.

To make use of this you add different settings to the different configuration files.  Once you do a build the proper settings will be moved from the Debug/Release configuration file to the actual Web.config file.

The Debug and Release configuration files give you instructions on how to make use of it.  Trust me, it is really easy!

<?xml version="1.0"?>
<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=301874 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <!--
    In the example below, the "SetAttributes" transform will change the value of
    "connectionString" to use "ReleaseSQLServer" only when the "Match" locator
    finds an attribute "name" that has a value of "MyDB".

    <connectionStrings>
      <add name="MyDB"
        connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
        xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
    </connectionStrings>
  -->
  <system.web>
    <!--
      In the example below, the "Replace" transform will replace the entire
      <customErrors> section of your Web.config file.
      Note that because there is only one customErrors section under the
      <system.web> node, there is no need to use the "xdt:Locator" attribute.

      <customErrors defaultRedirect="GenericError.htm"
        mode="RemoteOnly" xdt:Transform="Replace">
        <error statusCode="500" redirect="InternalError.htm"/>
      </customErrors>
    -->
  </system.web>
</configuration>

Conjuring it In Your Console Apps

Cicada Molting

I don’t like Cicadas, but I still took this picture because I thought it was neat.  Kind of like how I feel about console applications.  Adding this App.config transformation makes them a little easier to work with in the enterprise environment.  Not to mention a little cooler.

My main source to figure this out came from a Stackoverflow question.  It is my hope that I can lay it out a little better for you in this post.

When you first create your console application project you will have a single lonely App.config file.  In fact this file is going to be pretty empty with no hints at all about how to do transformations or that they are even possible.

Step 1 – Gather the Resources

The first thing you need to do is get the basic resources in place.  In this case it is simply adding some new XML files to the project.  To mimic the Web.config magic name them App.Debug.config and App.Release.config.App Config Files

When you add these XML files just rename them completely, including the xml extension.  You should end up with something like the image to the right of this text.  Three different files that are not related in anyway.

We will set that relationship up next.  So for now, save your project.

Step 2 – Edit the Project File

In order to edit the project file we must unload the project.  You can do this by right clicking the project in the solution explorer window and clicking on Unload Project.  If you have not already saved your project it will prompt you.

To edit the project file simply right click the project again and click on Edit ProjectName.csproj where ProjectName is the name of your project.  You should now have a rather large xml file open in your editor window.  Don’t worry about all that other stuff, right now our focus should be at the bottom of the file.  If this is no the case you can look through the file for the item section like you see below.

<ItemGroup>
  <None Include="App.config" />
  <None Include="App.Debug.config" />
  <None Include="App.Release.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->

Link the Files

We link the files by editing them just a bit.  We need to make each of them a content node, and add a dependency on the Debug and Release ones.  See the snippet below.

  <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>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <!-- 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="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->

Add the Magic

Now we need to add the actual magic.  Remember all that commented out stuff to modify the build process?  We are going to do something like that, but easier since it is such a basic change.  In fact you can copy and paste this xml over that commented out section.

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.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>

You should end up with something like this xml.

<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>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.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>

Save your work and close the file.  Right click the project in the solution explorer window and click Reload Project.  You should now see that your App.config can be expanded and looks very much like our Web.config.

AppConfigs

Harness the Magic

One of the first things we need to do is edit our App.Debug.config and App.Release.config.  Since they are fairly empty you can safely replace everything in them with this xml.  Notice the reference to the XML Document Transformation library.   From here it works like it does for the Web.config files.

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

</configuration>

Save your files.

See the Magic

I am going to edit the files a bit more as an example to show this in action.  I am going to add an appSettings node with one value in it.  It is a key of Target and a value will basically be whatever environment it is for; None, Debug, or Release.  When we build for the target environment we will see in the bin what is in the final configuration file.

Edited App Config Files

I will save my work and do a build for Debug and a build for Release.  Hopefully you can see in the image below the different configuration files from the appropriate bin folder.

ExeConfigs

Word of Caution

A quick word of caution though.  The Stackoverflow article was based off an earlier version of Visual Studio while I used Visual Studio 2015.  I ran into an error on my home machine that I did not get at work.  This was due to my work machine having different versions of Visual Studio on it compared to my home machine just running 2015.  The problem was the Microsoft.Web.Publishing.Tasks.dll location.  The old one looked in the v10.0 folder, I had to change that to look in the v14.0 folder.

Advertisements

About SheldonS

Web developer for over 15 years mainly with Microsoft technologies from classic ASP to .NET 4. Husband, father, and aspiring amateur photographer.

Posted on February 27, 2016, in .NET, Bits, Visual Studio and tagged , , . Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: