126

I have a dll-type project that contains MSTest integration tests. On my machine the tests pass, and I want the same to happen on a CI server (I use TeamCity). But the tests fail, because I need to tweak some settings in app.config. This is why I was thinking to have a separate second app.config file that will hold the settings for CI server.

So I would like to have

/Sln
 /Proj
  app.config (I think this is required by VS)
  app.Release.config (This is a standalone independent config file)

Thus if I select Release configuration in build config on CI, I would like to use app.Release.config file instead of app.config

Problem
This doesn't seem to be straightforward for simple .dll type projects. For web projects, I can do web config transformations. I found a hack how to do these transformations for a dll type project, but I am not a big fan of hacks.

Question
What is a standard approach to tweak app.config files depending on build config for .NET projects (such as Debug, Release, ...)?

oleksii
  • 35,458
  • 16
  • 93
  • 163

10 Answers10

157

Use SlowCheetah plugin. For more options and details of how to use SlowCheetah keep reading.

As you have already noticed, there is no default and easy way to use different config files for a Library type (.dll) project. The reason is that the current thinking is: "You don't need to"! Framework developers reckon you need configuration for the executable file: be it a console, desktop, web, mobile app or something else. If you start providing configuration for a dll, you may end up with something I can call a config hell. You may no longer understand (easily) why this and that variables have such weird values coming seemingly from nowhere.

"Hold on", - you may say, "but I need this for my integration/unit testing, and it is a library!". And that is true and this is what you can do (pick only one, don't mix):

1. SlowCheetah - transforms current config file

You can install SlowCheetah - a Visual Studio plug-in that does all low level XML poking (or transformation) for you. The way it works, briefly:

  • Install SlowCheetah and restart Visual Studio (Visual Studio > Tools > Extensions and Updates ... > Online > Visual Studio Gallery > search for "Slow Cheetah" )
  • Define your solution configurations (Debug and Release are there by default), you can add more (right click on the solution in Solution Explorer > Configuration Manager... > Active Solution Configuration > New...
  • Add a config file if needed
  • Right click on config file > Add Transform
    • This will create Transformation files - one per your configuration
    • Transform files work as injectors/mutators, they find needed XML code in the original config file and inject new lines or mutate needed value, whatever you tell it to do

2. Fiddle with .proj file - copy-renames a whole new config file

Originally taken from here. It's a custom MSBuild task that you can embed into Visual Studio .proj file. Copy and paste the following code into the project file

<Target Name="AfterBuild">
    <Delete Files="$(TargetDir)$(TargetFileName).config" />
    <Copy SourceFiles="$(ProjectDir)\Config\App.$(Configuration).config"
          DestinationFiles="$(TargetDir)$(TargetFileName).config" />
</Target>

Now create a folder in the project called Config and add new files there: App.Debug.config, App.Release.config and so on. Now, depending on your configuration, Visual Studio will pick the config file from a Config folder, and copy-rename it into the output directory. So if you had PatternPA.Test.Integration project and a Debug config selected, in the output folder after the build you will find a PatternPA.Test.Integration.dll.config file which was copied from Config\App.Debug.config and renamed afterwards.

These are some notes you can leave in the config files

<?xml version="1.0" encoding="utf-8"?>
<configuration>

    <!-- This file is copied and renamed by the 'AfterBuild' MSBuild task -->

    <!-- Depending on the configuration the content of projectName.dll.config 
        is fully substituted by the correspondent to build configuration file 
        from the 'Config' directory. -->

</configuration>

In Visual Studio you can have something like this

Project structure

3. Use scripting files outside Visual Studio

Each build tool (like NAnt, MSBuild) will provide capabilities to transform config file depending on the configuration. This is useful if you build your solution on a build machine, where you need to have more control on what and how you prepare the product for release.

For example you can use web publishing dll's task to transform any config file

<UsingTask AssemblyFile="..\tools\build\Microsoft.Web.Publishing.Tasks.dll"
    TaskName="TransformXml"/>

<PropertyGroup>
    <!-- Path to input config file -->  
    <TransformInputFile>path to app.config</TransformInputFile>
    <!-- Path to the transformation file -->    
    <TransformFile>path to app.$(Configuration).config</TransformFile>
    <!-- Path to outptu web config file --> 
    <TransformOutputFile>path to output project.dll.config</TransformOutputFile>
</PropertyGroup>

<Target Name="transform">
    <TransformXml Source="$(TransformInputFile)"
                  Transform="$(TransformFile)"
                  Destination="$(TransformOutputFile)" />
</Target>
Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
oleksii
  • 35,458
  • 16
  • 93
  • 163
  • Your second solution works fine, but not for publishing web projects. After publishing an ASP.NET project, the original web.config is published. – Masood Khaari Jun 25 '14 at 06:04
  • 3
    @MassoodKhaari you need to ensure this task is called for the publish target. When you publish a project a separate build target is called, which may not call by default `AfterBuild` target. During typical compilation the `AfterBuild` target is called by default. There should be a quick fix for the publish case – oleksii Jun 25 '14 at 10:51
  • 1
    Used your second method (kinda). Went to project properties and edited the **BeforeBuild** to copy the `App..config` over the `App.config` in the **project dir**, not the output dir. – SparK Jun 27 '14 at 15:22
  • @oleksii You're right. But I couldn't still find the target my web publish process is using (in Visual Studio 2013). – Masood Khaari Jun 28 '14 at 04:09
  • I found a solution that is similar to your third solution but I think that it's easier and cleaner: http://philbolduc.blogspot.com.ar/2010/03/using-config-transforms-outside-web.html – Francisco Goldenstein Jul 28 '14 at 20:46
  • 3
    I am using the second method, but needed to add a condition to the AfterBuild target to ensure the file actually exists before deleting. I have a Debug build configuration, which basically just uses the default App.config file, but I had no App.Debug.config, which meant the build step would fail. I just added `Condition="Exists('$(ProjectDir)App.$(Configuration).config')"`. – Siewers Feb 04 '16 at 15:40
  • @MassoodKhaari, where you able to find the target for the publish? I am having same problem with my app.config file? – Kashif Qureshi Jul 23 '17 at 11:12
  • @KashifQureshi Unfortunately I can't recall, as the discussion is quite old. – Masood Khaari Jul 23 '17 at 14:52
  • The Slow Cheetah extension no longer seems to work in VS2019, as of v16.9.4. At least, not for the configsource config files I needed to transform. – samp Apr 29 '21 at 20:14
25

You can try the following approach:

  1. Right-click on the project in Solution Explorer and select Unload Project.
  2. The project will be unloaded. Right-click on the project again and select Edit <YourProjectName>.csproj.
  3. Now you can edit the project file inside Visual Studio.
  4. Locate the place in *.csproj file where your application configuration file is included. It will look like:
    <ItemGroup>
        <None Include="App.config"/>
    </ItemGroup>
  1. Replace this lines with following:
    <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
        <None Include="App.Debug.config"/>
    </ItemGroup>

    <ItemGroup Condition=" '$(Configuration)' == 'Release' ">
        <None Include="App.Release.config"/>
    </ItemGroup>

I have not tried this approach to app.config files, but it worked fine with other items of Visual Studio projects. You can customize the build process in almost any way you like. Anyway, let me know the result.

vharavy
  • 4,881
  • 23
  • 30
  • Tnx for the answer, but this doesn't work with app.config. VS requires a mandatory `app.config` and is not applying Release config if I use VS build or Teamcity VS sln build runner. – oleksii Nov 10 '11 at 21:18
  • 2
    Here explains how to do it: [Enable app.debug.config app.release.config](http://mitasoft.wordpress.com/2011/09/28/multipleappconfig/) – Gabrielizalo Mar 05 '14 at 20:39
  • 1
    Why does this answer have so many up votes? I tried it and it does not work. In fact in both debug and release mode there is no App.config file and therefore, there is no corresponding file in the output folder. The files App.Debug.config and App.Release.config do not have any meaning for Visual Studio. – MarkusParker Nov 20 '17 at 09:07
  • It doesn't work : .csproj can't be opened, error message "elements outside Target elements must have : Include, Update or Remove" – Elo May 09 '19 at 09:39
13

Using the same as approach as Romeo, I adapted it to Visual Studio 2010 :

 <None Condition=" '$(Configuration)' == 'Debug' " Include="appDebug\App.config" />

 <None Condition=" '$(Configuration)' == 'Release' " Include="appRelease\App.config" />

Here you need to keep both App.config files in different directories (appDebug and appRelease). I tested it and it works fine!

neural5torm
  • 773
  • 1
  • 9
  • 21
tem peru
  • 131
  • 1
  • 2
12

You should consider ConfigGen. It was developed for this purpose. It produces a config file for each deployment machine, based on a template file and a settings file. I know that this doesn't answer your question specifically, but it might well answer your problem.

So rather than Debug, Release etc, you might have Test, UAT, Production etc. You can also have different settings for each developer machine, so that you can generate a config specific to your dev machine and change it without affecting any one else's deployment.

An example of usage might be...

<Target Name="BeforeBuild">
    <Exec Command="C:\Tools\cfg -s $(ProjectDir)App.Config.Settings.xls -t       
        $(ProjectDir)App.config.template.xml -o $(SolutionDir)ConfigGen" />

    <Exec Command="C:\Tools\cfg -s $(ProjectDir)App.Config.Settings.xls -t
        $(ProjectDir)App.config.template.xml -l -n $(ProjectDir)App.config" />
</Target>

If you place this in your .csproj file, and you have the following files...

$(ProjectDir)App.Config.Settings.xls

MachineName        ConfigFilePath   SQLServer        

default             App.config      DEVSQL005
Test                App.config      TESTSQL005
UAT                 App.config      UATSQL005
Production          App.config      PRODSQL005
YourLocalMachine    App.config      ./SQLEXPRESS


$(ProjectDir)App.config.template.xml 

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
   <configuration>
   <appSettings>
       <add key="ConnectionString" value="Data Source=[%SQLServer%]; 
           Database=DatabaseName; Trusted_Connection=True"/>
   </appSettings>
</configuration>

... then this will be the result...

From the first command, a config file generated for each environment specified in the xls file, placed in the output directory $(SolutionDir)ConfigGen

.../solutiondir/ConfigGen/Production/App.config

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
   <configuration>
   <appSettings>
       <add key="ConnectionString" value="Data Source=PRODSQL005; 
           Database=DatabaseName; Trusted_Connection=True"/>
   </appSettings>
</configuration>

From the second command, the local App.config used on your dev machine will be replaced with the generated config specified by the local (-l) switch and the filename (-n) switch.

Daniel Dyson
  • 13,192
  • 6
  • 42
  • 73
  • 2
    Tnx for the answer, this looks not bad. But there are some drawbacks, it shows only 75 downloads (thus it's not mature) and it works with .xls or .xlsx only. I don't really want to depend on yet another custom document format for simple operations. I was looking for a more standard approach... – oleksii Nov 10 '11 at 19:51
  • 2
    Fair point, although it says 194 downloads on CodePlex, xls is a spreadsheet, hardly a custom format, and I know of three Major Investment Banks that have approved this for use, so if it is good enough for them... Also, one of the features currently requested is to use xml for the settings. It is nearly ready, but I prefer the spreadsheet approach anyway. It is much easier to see every setting for every environment in a tabular view – Daniel Dyson Nov 10 '11 at 22:17
  • We are now in the final stages of testing a version of configGen that can be used to generate plain text files, not just xml. So if you want to generate environment-specific css, sql, javascript etc, keep an eye on the configGen site – Daniel Dyson Aug 08 '12 at 08:27
  • thanks Daniel for the solution, this is exactly what i was looking for. I will give it a try. – Bhupinder Singh May 30 '17 at 13:46
8

I have heard good things about SlowCheetah, but was unable to get it to work. I did the following: add am tag to each for a specific configuration.

Ex:

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'UAT|AnyCPU'">
    <OutputPath>bin\UAT\</OutputPath>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <AppConfig>App.UAT.config</AppConfig>
  </PropertyGroup>
Mike
  • 629
  • 5
  • 18
  • This seems like another super simple way to change app.config files per the build configuration. Mike, did you test with the standard Debug and Release Configurations? – BoiseBaked Oct 30 '18 at 19:02
  • 1
    4 years later, this helped me. Thank you. – Russ Mar 24 '22 at 01:19
3

SlowCheetah and FastKoala from the VisualStudio Gallery seem to be very good tools that help out with this problem.

However, if you want to avoid addins or use the principles they implement more extensively throughout your build/integration processes then adding this to your msbuild *proj files is a shorthand fix.

Note: this is more or less a rework of the No. 2 of @oleksii's answer.

This works for .exe and .dll projects:

  <Target Name="TransformOnBuild" BeforeTargets="PrepareForBuild">
    <TransformXml Source="App_Config\app.Base.config" Transform="App_Config\app.$(Configuration).config" Destination="app.config" />
  </Target>

This works for web projects:

  <Target Name="TransformOnBuild" BeforeTargets="PrepareForBuild">
    <TransformXml Source="App_Config\Web.Base.config" Transform="App_Config\Web.$(Configuration).config" Destination="Web.config" />
  </Target>

Note that this step happens even before the build proper begins. The transformation of the config file happens in the project folder. So that the transformed web.config is available when you are debugging (a drawback of SlowCheetah).

Do remember that if you create the App_Config folder (or whatever you choose to call it), the various intermediate config files should have a Build Action = None, and Copy to Output Directory = Do not copy.

This combines both options into one block. The appropriate one is executed based on conditions. The TransformXml task is defined first though:

<Project>
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="TransformOnBuild" BeforeTargets="PrepareForBuild">
    <TransformXml Condition="Exists('App_Config\app.Base.config')" Source="App_Config\app.Base.config" Transform="App_Config\app.$(Configuration).config" Destination="app.config" />
    <TransformXml Condition="Exists('App_Config\Web.Base.config')" Source="App_Config\Web.Base.config" Transform="App_Config\Web.$(Configuration).config" Destination="Web.config" />
</Target>

Eniola
  • 710
  • 1
  • 7
  • 23
  • I just tried this in Visual Studio 2017 and it's not working. Shoot. I was really hoping it would work, because it looks like the easiest to implement. – Greg Burghardt May 10 '18 at 14:53
  • The TransformXml task is not defined in the samples. I am adding an entry. You can define it in a mycustom.targets file that gets included in all your launch projects in your solution. – Eniola May 11 '18 at 08:03
  • @GregBurghardt, do you want to try it now? – Eniola May 11 '18 at 08:12
  • I might give it a try. I installed the Config Transform plugin for Visual Studio, and that worked really well. I actually wonder if the plugin basically does what your answer does. – Greg Burghardt May 11 '18 at 11:46
  • Okay, let me know how it goes. – Eniola May 14 '18 at 20:14
3

I'm using XmlPreprocess tool for config files manipulation. It is using one mapping file for multiple environments(or multiple build targets in your case). You can edit mapping file by Excel. It is very easy to use.

Ludwo
  • 6,043
  • 4
  • 32
  • 48
2

I have solved this topic with the solution I have found here: http://www.blackwasp.co.uk/SwitchConfig.aspx

In short what they state there is: "by adding a post-build event.[...] We need to add the following:

if "Debug"=="$(ConfigurationName)" goto :nocopy
del "$(TargetPath).config"
copy "$(ProjectDir)\Release.config" "$(TargetPath).config"
:nocopy
Janbro
  • 69
  • 6
  • 1
    By far the easiest method to do what should have been a very simple and essential function that was screwed-up by over-thinkers! Thanks Janbro. – BoiseBaked Oct 30 '18 at 17:42
1

See if the XDT (web.config) transform engine can help you. Currently it's only natively supported for web projects, but technically there is nothing stopping you from using it in other application types. There are many guides on how to use XDT by manually editing the project files, but I found a plugin that works great: https://visualstudiogallery.msdn.microsoft.com/579d3a78-3bdd-497c-bc21-aa6e6abbc859

The plugin is only helping to setup the configuration, it's not needed to build and the solution can be built on other machines or on a build server without the plugin or any other tools being required.

TGasdf
  • 1,220
  • 12
  • 14
  • This should be the answer now. Just tried it out on VS 2017 and it works like a charm. You don't need to publish the project. Just build it. Works great for our test project for use in our continuous integration build so we can run Selenium tests in headless mode, yet locally they run with the browser opening up. +1,000,000 if I could. – Greg Burghardt May 10 '18 at 15:13
0

After some research on managing configs for development and builds etc, I decided to roll my own, I have made it available on bitbucket at: https://bitbucket.org/brightertools/contemplate/wiki/Home

This multiple configuration files for multiple environments, its a basic configuration entry replacement tool that will work with any text based file format.

Hope this helps.

Mark Redman
  • 24,079
  • 20
  • 92
  • 147