Separate configuration files per build config in Visual Studio IDE

Generating different configuration files on the build time in VS IDE

It is an often case that application configuration is different for each environment. Most often this is the case for database connection string where developer has connection string to dev environment database or it's local database and quality assurance (qa) and production have different connection strings wile the rest of the config. file is the same.

Different config may be also required if you have an app that runs in different geo regions and they access different storages which are located in the same region to reduce latency and therefore application response which is typical for Web applications. In all these cases your application code is the same, it is just different pieces of configuration you need to run them with.

This can be done through different build configuration in Visual Studio and different config transformations can be applied to different build configs. Visual Studio IDE provides this ability throug configuration manager interface. Configuration manager window is available from Build drop-down menu item.

Note

More about how to add build configurations can be found at MSDN online documentation https://msdn.microsoft.com/en-us/library/kwybya3w.aspx

This applies to all types of executable projects in Visual Studio IDE, while there is a different GUI support. For example I found option to generate a different config transformation for ASP.NET project while I did not have such option for console applications or windows service projects.

Even though configuration transformations option is not available for every project type in VS there are community extension which can be installed to solve this. One of them can be found at Visual Studio Market place and it does a great job for this king of requirement https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform.

Since App.config as well as Web.config are XML files, transformation is done through XDT. Available transformation options are described in details on MSDN online documentation https://msdn.microsoft.com/en-us/library/dd465326(v=vs.110).aspx.

Running the following sample code on different build configurations will print out the different values.

using System;
using System.Configuration;

namespace ConfigurationTransformSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(ConfigurationManager.AppSettings["region"]);
            Console.WriteLine(ConfigurationManager.AppSettings["mode"]);
            Console.WriteLine(ConfigurationManager.AppSettings["environment"]);
            Console.ReadLine();
        }
    }
}

    

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>
  <appSettings>
    <add key="environment" value="local"/>
    <add key="mode" value="debug"/>
  </appSettings>
</configuration>
    

Once we run application in with Debug build configuration, since we did not update App.Debug.config, configuration from App.config will be applied and result of a run in will be the following:


debug
local

We will use transformations for AppSetings key value. For the example we will use simple console application and create additional two build definitions using Release build definitions as a template. So we will have the following build definitions in our project:

  • Debug
  • Release
  • Release-EU
  • Release-US

This is first done through Build Configuration window from Build menu and once this is done, transformation files are created from the context menu option on the App.config file which is provided from previously installed extension.

build-config

Once we have different config file, we'll add a key to App.config and then transform it in App.Release-EU.config and App.Release-US.config.

App.Release-EU.config

<?xml version="1.0"?>
<!-- For more information on using app.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="environment" value="production" xdt:Transform="SetAttributes(value)" xdt:Locator="Match(key)" />
    <add key="region" value="EUROPE" xdt:Transform="Insert"/>
    <add key="mode" xdt:Transform="Remove" xdt:Locator="Match(key)"/>
  </appSettings>
</configuration>
    

If we run the application with Release-EU confihuration we will get the following output

EUROPE

production

App.Release-US.config

<?xml version="1.0"?>
<!-- For more information on using app.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="mode" value="production"  xdt:Transform="SetAttributes(value)" xdt:Locator="Match(key)" />
    <add key="region" value="USA" xdt:Transform="Insert"/>
    <add key="mode" xdt:Transform="Remove" xdt:Locator="Match(key)"/>
  </appSettings>
</configuration>
    

The run with Release-US build configuration will display the following result in the console

USA

production

Different ways of matching the element for transformation with Locator attribute

In previous sample we only used Match selector. However, there are other ways to match elements for transformation. The following are options to match elements for transformation with the sample transformation snippet based on the configuration transformations used in the the sample.

It demonstrates different ways to match same element for transformation.

Condition 

Match element based on XPath expression that takes the current element as a current path (relative path)
XPath

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="environment" value="production" xdt:Transform="SetAttributes(value)" xdt:Locator="Condition(@key='environment')" />
  </appSettings>
</configuration>
    

XPath

Match element based on XPath expression that considers document root as a starting point. Basically XPath expression needs to contain absolute XML document path

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
<add key="environment" value="production" xdt:Transform="SetAttributes(value)" xdt:Locator="XPath(/configuration/appSettings/add[@key='environment'])" />
  </appSettings>
</configuration>
    

Transform attribute options 

  • Replace - Replaces the first element matching Locator attribute selector with the element declared in the transformation
  • Insert - Adds the element as a sibling of the element matching the Locator value at the end of the parent element
  • InsertBefore - Adds the element as a sibling before of the element matching the Locator value
  • InsertAfter - Adds the element as a sibling after of the element matching the Locator value
  • Remove - Removes the first element matching the Locator attribute expression
  • RemoveAll - Removes element containing matching Locator expression element
  • RemoveAttributes - Removes singe or multiple attributes (comma separated) of the matching element

Build configuration can be passed as a parameter to msbuild, so you can use tis to generate different build for different environment if you are setting up Continuous Integration for example in Microsoft Team Services.

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article