App.Config Transformation for projects which are not Web Projects in Visual Studio?
Asked Answered
P

16

589

For Visual Studio 2010 Web based application we have Config Transformation features by which we can maintain multiple configuration files for different environments. But the same feature is not available for App.Config files for Windows Services/WinForms or Console Application.

There is a workaround available as suggested here: Applying XDT magic to App.Config.

However it is not straightforward and requires a number of steps. Is there an easier way to achieve the same for app.config files?

Physical answered 9/6, 2010 at 8:38 Comment(1)
I've come across the following article which looks a bit simpler but I've not tried it myself. fknut.blogspot.com/2009/11/… Also, there is a feature request on MS Connect which might be worth voting up so this gets included across the board in the next SP or version. connect.microsoft.com/VisualStudio/feedback/details/564414Celibate
S
427

This works now with the Visual Studio AddIn treated in this article: SlowCheetah - Web.config Transformation Syntax now generalized for any XML configuration file.

You can right-click on your web.config and click "Add Config Transforms." When you do this, you'll get a web.debug.config and a web.release.config. You can make a web.whatever.config if you like, as long as the name lines up with a configuration profile. These files are just the changes you want made, not a complete copy of your web.config.

You might think you'd want to use XSLT to transform a web.config, but while they feels intuitively right it's actually very verbose.

Here's two transforms, one using XSLT and the same one using the XML Document Transform syntax/namespace. As with all things there's multiple ways in XSLT to do this, but you get the general idea. XSLT is a generalized tree transformation language, while this deployment one is optimized for a specific subset of common scenarios. But, the cool part is that each XDT transform is a .NET plugin, so you can make your own.

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
  <xsl:copy>           
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="/configuration/appSettings">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    <xsl:element name="add">
      <xsl:attribute name="key">NewSetting</xsl:attribute>
      <xsl:attribute name="value">New Setting Value</xsl:attribute>
    </xsl:element>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Or the same thing via the deployment transform:

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
   <appSettings>
      <add name="NewSetting" value="New Setting Value" xdt:Transform="Insert"/>
   </appSettings>
</configuration>
Striped answered 25/8, 2011 at 18:15 Comment(7)
Oh that is sweet! Have an app with numerous config files (log4net, nHibernate, web.config) and remembering to change them all was a bit of a pain. I was not looking forward to moving the code into CruiseControl.NET either but looks like that's a breeze too.Mutton
FYI, SlowCheetah was a fantastic extension that will now be unsupported after VS 2014. Per the author, Sayed Ibrahim Hashimi, sedodream.com/2014/08/11/….Accipiter
@AnilNatha Where does it say they'll be dropping support?Gouveia
@andrewb, I read that here; however, that was a year ago. After revisiting the thread, and reading the comments, looks like someone has provided a version that works with VS2015 here.Roper
Working perfectly with Visual Studio 2017 and Visual STudio 2019Cheryl
It's here nowRoswell
Does anyone know where to get a version of this which is compatible with Visual Studio 2010?Brycebryn
K
641

I tried several solutions and here is the simplest I personally found.
Dan pointed out in the comments that the original post belongs to Oleg Sychthanks, Oleg!

Here are the instructions:

1. Add an XML file for each configuration to the project.

Typically you will have Debug and Release configurations so name your files App.Debug.config and App.Release.config. In my project, I created a configuration for each kind of environment, so you might want to experiment with that.

2. Unload project and open .csproj file for editing

Visual Studio allows you to edit .csproj files right in the editor—you just need to unload the project first. Then right-click on it and select Edit <ProjectName>.csproj.

3. Bind App.*.config files to main App.config

Find the project file section that contains all App.config and App.*.config references. You'll notice their build actions are set to None and that's okay:

<None Include="App.config" />
<None Include="App.Debug.config" />
<None Include="App.Release.config" />

Next, make all configuration-specific files dependant on the main App.config so Visual Studio groups them like it does designer and code-behind files.

Replace XML above with the one below:

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

4. Activate transformations magic (still necessary for Visual Studio versions such as VS2019)

In the end of file after

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

and before final

</Project>

insert the following XML -- please note there are two steps for the proper transformation to occur:

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="BeforeBuild" Condition="Exists('App.$(Configuration).config')">
    <!-- Generate transformed app config and replace it: will get the <runtime> node and assembly bindings properly populated -->
    <TransformXml Source="App.config" Destination="App.config" Transform="App.$(Configuration).config" />
  </Target>
  <Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory: this will transform sections such as appSettings -->
    <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>

Now you can reload the project, build it and enjoy App.config transformations!

FYI

Make sure that your App.*.config files have the right setup like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
     <!--magic transformations here-->
</configuration>
Knack answered 24/2, 2011 at 19:36 Comment(45)
Thanks a ton for this! One note, if you add the new .config files to the project after you edit the csproj, they'll show up grouped under App.config. I added one before editing the csproj and essentially ended up with two links to it, one grouped and one solo.Implement
Are you sure that you removed all original <None Include=""/>s before adding new <Content Include=""/>s? This instruction worked for me several times so perhaps you could've missed something.Knack
I removed all of the <None>'s related to .config files. Did I need to do it for other resources as well?Implement
Don't think so. Well, anyway I suppose you have the issue solved now?Knack
@gaearon: Thanks a lot; I've been looking for this for a while - wish I could give more than one upvote.Villeinage
Where does the $(TargetFileName) property come from? I'm trying to use this to transform more than just the web.config in my webapp project.Dipper
@Maslow: I think that for windows projects it contains output file name (e.g. SampleApp.exe). Therefore, $(TargetFileName).config becomes SampleApp.exe.config, which is exactly the file we wanted to transform. You wouldn't need that for web project because it doesn't have a single output file name.Knack
I've almost got this working. The file in the /obj folder is correct, but it's not copied as the final output file. I believe that means the AppConfigWithTargetPath bits aren't working for me - is there anything I can do?Endocrinotherapy
@Neil: Can you try creating a new project and reproducing the problem?Knack
I fixed my problem it by changing AfterCompile to BeforeCompile, in the end. To be honest, that would seem to make sense, but I'm not well-up on the chain of commands for the build process.Endocrinotherapy
One problem with this approach is that when you look over at the "Publish" tab in the project properties, and then click on the "Application Files" button... you'll notice that app.config, app.Debug.config, app.Release.config are forced to be deployed as part of the Publish process. Sure, you get the correct MyApp.exe.config file too, but I don't want that extra baggage getting deployed. There needs to be a way to keep the app.*.config files in the project as <None> instead of <Content>.Leotie
When using a Setup & Deployment Project to deploy a Windows application, the transformed config file is not being deployed. The S&D picks app.config (renames to AppName.exe.config) and deploys that instead. I am still working out how to fix this...anyone have idea's?Adalie
Just to add to my previous comment, I checked the bin and obj folders and they contain the correctly transformed config file but S&D is not using it.Adalie
In Target name, AfterCompile didn't work for me, AfterBuild did.Overleap
Works great, this is the only solution with VS Express Edition.Tompion
This should still be the correct answer since the aforementioned addon "SlowCheetah" appears to be unrealiable.Duchamp
Thanks, this is the way to go. I've been trying to use SlowCheetah in our team but it was unrealiable most of the time.Vanish
An Oscar winner answer. Specially the "Insert the following XML" section.Athwart
This could possibly be re-updated to no longer refer to slow cheetah, due to its oncoming demise. (its also cleaner IMHO than burdening VS with another add-on.)Youngster
Not working for me, I did same and when I publish package as zip file then inside that my app.config file is never transformed. It remains same.Teatime
The one problem this is leaving out for some is the answer originally taken from Oleg Sych leaves out a key piece. If in your individual app.(env).configs you DO NOT list '<configuration xmlns:xdt="schemas.microsoft.com/XML-Document-Transform">' and something like <appSettings xdt:Transform="Replace"> or attributes that do similar things on the setting lines, it will not work. This last bit of information is key and once I added it, poof it all started working.Chinese
ahhhhhh... relief at last!!! FWIW - applied this answer successfully to my VB.NET project in V.S. 2015.. super easy!Lorant
You could replace v10.0 with v$(VisualStudioVersion) to make sure your project does work with all later versions of VS.Doerrer
I realize this is five years later but this still seems the best solution with @ThibaultD.'s v$(VisualStudioVersion) suggestion, and without changing the <None /> tags to <Content />Ashla
Don't give me credit for the v$(VisualStudioVersion), I just copied it from this answer: https://mcmap.net/q/64339/-app-config-transformation-for-projects-which-are-not-web-projects-in-visual-studio because I thought it would be better of as a comment.Doerrer
One important note here... don't forget to add <tagname xdt:Transform="Replace" >...</tagname> inside your app.release.config file tags that should be used to replace the debug settings.Lipoma
In the first line, you can even use this expression $(VSToolsPath)\Web\Microsoft.Web.Publishing.Ta‌​sks.dll instead of this one $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll.Dagoba
I had an error MSBuild error MSB3021: Unable to copy file. Could not find file 'obj\Release\ConsoleApp.exe' during the build. So I change a bit the solution to reuse target <Target Name="AfterBuild"> section instead of creating a new like in the solutionNeuroma
I have not tested this in earlier versions of VS, but in 2019 I had to put the <UsingTask> and <Target> bits before the existing CSharp target. This was for a WPF app, but it was failing to compile the project not recognizing much of the code and it was also not recognizing any of the project references to other projects in the solution.Bijugate
The key here was to convert Content type, it seems transformations do not occur on None type.Barbuto
another comment, this did not work well in Azure Pipelines, what I did in the end, was to use FileTransform task which applies the same transformation, before building the solution, and also disabling it from all csprojs. In some way, it makes sense to do at CI time, and also is more transparent for the build process.Barbuto
... In some way, it makes sense to do at CI time as it is not compilation per se, it's also more transparent for the build process, and plus, that TransformXml call sometimes did not compile in local environment (probably VS versions mixup).Barbuto
As @KatLimRuiz indicated, it is better to do this in the release pipeline using the official File Tranform task. I've put an answer on another question, which specifically asks how to do this on Azure DevOps, detailing how to do this using said task.Englert
I just came back to this again after a few years, and I wish I could upvote again.Kaylyn
For newcomers, you don't need to do all of this anymore, at least in VS2017. Just change the from None to Content and you can also do this: <ItemGroup> <Content Update="App.*.config" DependentUpon="App.config" /> </ItemGroup>, instead of adding to each App.*.config entry.Loaiasis
@Loaiasis I'm editing the answer to include your commentMarthmartha
@EstebanVerbel, @jpgrassi: When you say you don't have to do step 4 anymore, does that mean not including the CoreCompile target? Is that now automatically performed?Setaceous
@Setaceous I tried it without step 4 but couldnt get it to work. So I just included step 4. I am using VS 2019Trover
For some reason mine is not working, When I run debug it just gives me the app.config values?Foppish
I'm on VS2019 and it will only work with step 4 included, but that causes errors if you haven't already built the application once. If you remove the obj folder as if building from scratch then you can't build due to files being missing.Areta
@D.Hodge If you change CoreCompile to AfterCompile it works fine.Interdisciplinary
On the VS19, don't forget to change the 4th step. <Target Name="CoreCompile" to <Target Name="AfterCompile". Otherwise, it will raise a build error: Could not copy the file "obj\Debug\*.exe" because it was not found.Diagraph
Nice. Also add <Target Name="CleanTransforms" AfterTargets="Clean"><Delete Files="$(IntermediateOutputPath)$(TargetFileName).config" /></Target> so Clean still cleans.Torment
Is it possible to preview the transform like you can in a .net web application when doing transforms on web.config?Ringleader
@DanAbramov Abramov Also, how does this work when you use Azure to deploy releases? The build will always just build a single MyService.dll.config. It doesn't give you one for each build to release to each specific server with the specific settings.Ringleader
S
427

This works now with the Visual Studio AddIn treated in this article: SlowCheetah - Web.config Transformation Syntax now generalized for any XML configuration file.

You can right-click on your web.config and click "Add Config Transforms." When you do this, you'll get a web.debug.config and a web.release.config. You can make a web.whatever.config if you like, as long as the name lines up with a configuration profile. These files are just the changes you want made, not a complete copy of your web.config.

You might think you'd want to use XSLT to transform a web.config, but while they feels intuitively right it's actually very verbose.

Here's two transforms, one using XSLT and the same one using the XML Document Transform syntax/namespace. As with all things there's multiple ways in XSLT to do this, but you get the general idea. XSLT is a generalized tree transformation language, while this deployment one is optimized for a specific subset of common scenarios. But, the cool part is that each XDT transform is a .NET plugin, so you can make your own.

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
  <xsl:copy>           
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="/configuration/appSettings">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    <xsl:element name="add">
      <xsl:attribute name="key">NewSetting</xsl:attribute>
      <xsl:attribute name="value">New Setting Value</xsl:attribute>
    </xsl:element>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Or the same thing via the deployment transform:

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
   <appSettings>
      <add name="NewSetting" value="New Setting Value" xdt:Transform="Insert"/>
   </appSettings>
</configuration>
Striped answered 25/8, 2011 at 18:15 Comment(7)
Oh that is sweet! Have an app with numerous config files (log4net, nHibernate, web.config) and remembering to change them all was a bit of a pain. I was not looking forward to moving the code into CruiseControl.NET either but looks like that's a breeze too.Mutton
FYI, SlowCheetah was a fantastic extension that will now be unsupported after VS 2014. Per the author, Sayed Ibrahim Hashimi, sedodream.com/2014/08/11/….Accipiter
@AnilNatha Where does it say they'll be dropping support?Gouveia
@andrewb, I read that here; however, that was a year ago. After revisiting the thread, and reading the comments, looks like someone has provided a version that works with VS2015 here.Roper
Working perfectly with Visual Studio 2017 and Visual STudio 2019Cheryl
It's here nowRoswell
Does anyone know where to get a version of this which is compatible with Visual Studio 2010?Brycebryn
A
149

Another solution I've found is NOT to use the transformations but just have a separate config file, e.g. app.Release.config. Then add this line to your csproj file.

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <AppConfig>App.Release.config</AppConfig>
  </PropertyGroup>

This will not only generate the right myprogram.exe.config file but if you're using Setup and Deployment Project in Visual Studio to generate MSI, it'll force the deployment project to use the correct config file when packaging.

Artair answered 18/10, 2011 at 18:12 Comment(8)
The untold wonders of MSBuild. Now I wonder what else is possible. Btw. this works also for clickonce deployments directly from VS (in contrast to higher-voted answers).Rysler
Changes can become onerous and error prone if the configs contain many entries that are the SAME for all builds. Dealing with an issue right now where one environment's .config missed a change, and of course it was production.Airiness
Having two copies of config file is not a problem, as long as developers are not the ones who manually maintain it.Epigraph
This is beautiful, works like a charm! I pasted just the <AppConfig>App.Release.config</AppConfig> line inside the existing <PropertyGroup condition for the Release configuration and the IDE showed a squiggly line below the <AppConfig>... line saying it was not in the schema or something, but I saved the file anyways and reloaded the project file and did a build in Release config and it worked!Marlysmarmaduke
With this, you will lose functionality of settings designer.Sura
Just an addition: Of course you can change the condition ignoring the Platform (e.g. if you build "Any Platform"): <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">Bimestrial
This works (very easily) and is IMHO the best option for simple workflows.Labrecque
For simple workflows yes, as having to clone large config files over and over again only to change 1 value is not feasible.Slipcover
A
39

Inspired by Oleg and others in this question, I took the solution https://mcmap.net/q/64339/-app-config-transformation-for-projects-which-are-not-web-projects-in-visual-studio a step further to enable the following.

  • Works with ClickOnce
  • Works with Setup and Deployment projects in VS 2010
  • Works with VS2010, 2013, 2015 (didn't test 2012 although should work as well).
  • Works with Team Build. (You must install either A) Visual Studio or B) Microsoft.Web.Publishing.targets and Microsoft.Web.Publishing.Tasks.dll)

This solution works by performing the app.config transformation before the app.config is referenced for the first time in the MSBuild process. It uses an external targets file for easier management across multiple projects.

Instructions:

Similar steps to the other solution. I've quoted what remains the same and included it for completeness and easier comparison.

0. Add a new file to your project called AppConfigTransformation.targets

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Transform the app config per project configuration.-->
  <PropertyGroup>
    <!-- This ensures compatibility across multiple versions of Visual Studio when using a solution file.
         However, when using MSBuild directly you may need to override this property to 11.0 or 12.0 
         accordingly as part of the MSBuild script, ie /p:VisualStudioVersion=11.0;
         See http://blogs.msdn.com/b/webdev/archive/2012/08/22/visual-studio-project-compatability-and-visualstudioversion.aspx -->
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
  </PropertyGroup>

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.targets" />

  <Target Name="SetTransformAppConfigDestination" BeforeTargets="PrepareForBuild" 
          Condition="exists('app.$(Configuration).config')">
    <PropertyGroup>
      <!-- Force build process to use the transformed configuration file from now on. -->
      <AppConfig>$(IntermediateOutputPath)$(TargetFileName).config</AppConfig>
    </PropertyGroup>
    <Message Text="AppConfig transformation destination: = $(AppConfig)" />
  </Target>

  <!-- Transform the app.config after the prepare for build completes. -->
  <Target Name="TransformAppConfig" AfterTargets="PrepareForBuild" Condition="exists('app.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory -->
    <TransformXml Source="app.config" Destination="$(AppConfig)" Transform="app.$(Configuration).config" />
  </Target>

</Project>

1. Add an XML file for each configuration to the project.

Typically you will have Debug and Release configurations so name your files App.Debug.config and App.Release.config. In my project, I created a configuration for each kind of enironment so you might want to experiment with that.

2. Unload project and open .csproj file for editing

Visual Studio allows you to edit .csproj right in the editor—you just need to unload the project first. Then right-click on it and select Edit .csproj.

3. Bind App.*.config files to main App.config

Find the project file section that contains all App.config and App.*.config references and replace as follows. You'll notice we use None instead of Content.

<ItemGroup>
  <None Include="app.config"/>
  <None Include="app.Production.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
  <None Include="app.QA.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
  <None Include="app.Development.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
</ItemGroup>

4. Activate transformations magic

In the end of file after

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

and before final

</Project>

insert the following XML:

<Import Project="AppConfigTransformation.targets" />

Done!

Accipiter answered 1/10, 2014 at 23:23 Comment(7)
Tried in VS Community 2015 RC and it ignores the app.Debug.config file I have.Ur
I successfully used the accepted answer on one WinForms project .. but for some baffling reason couldn't apply the accepted ans. to another WinForms project (all in the same solution). This answer from @Accipiter is my new fave - as it correctly interoperated with my MSI project - big thanks!Lorant
This didn't seem to work in VS 2015. I updated the VisualStudioVersion from 10 to 12 but no dice. Any ideas?Eads
@Eads Can you give us more details? VS 2015 Ultimate, Community, etc. VB.NET, C#, Any errors?Accipiter
VS2015 Enterprise. No errors whatsoever. It just doesn't do anything.Eads
1) Make sure you include the file as part of the project. Build Action = None. 2) Check your transformation file. It's possible the transformation is incorrect.Accipiter
Hi, I tried to follow above mentioned steps in my VSTO project type, but unfortunately I could not able to get the transformations in my main app.config file. BTW, I could see that it created the config file as App.config.deploy. Is there some thing I am missing or doing wrong - Here is my config file and CS Project file (gist.github.com/Ganeshcse/f3a7369d93b6b06923e9aa848682fa06)Sibylle
E
35

In my experience, the things I need to make environment-specific are things like connection strings, appsettings and often smpt settings. The config system allows to specify these things in separate files. So you can use this in your app.config/web.config:

 <appSettings configSource="appsettings.config" />
 <connectionStrings configSource="connection.config" />
 <system.net>
    <mailSettings>
       <smtp configSource="smtp.config"/>
    </mailSettings>
 </system.net>

What I typically do is to put these config-specific sections in separate files, in a subfolder called ConfigFiles (either in the solution root or at the project level, depends). I define a file per configuration, e.g. smtp.config.Debug and smtp.config.Release.

Then you can define a pre-build event like so:

copy $(ProjectDir)ConfigFiles\smtp.config.$(ConfigurationName) $(TargetDir)smtp.config

In team development, you can tweak this further by including the %COMPUTERNAME% and/or %USERNAME% in the convention.

Of course, this implies that the target files (x.config) should NOT be put in source control (since they are generated). You should still add them to the project file and set their output type property to 'copy always' or 'copy if newer' though.

Simple, extensible, and it works for all types of Visual Studio projects (console, winforms, wpf, web).

Engrave answered 12/2, 2011 at 10:8 Comment(4)
I have exactly the same configuration that you have. But I have problems transforming the smtp file. Can you incluye the original and tranformation? These are mine: The base file: <?xml version="1.0"?> <smtp deliveryMethod="SpecifiedPickupDirectory"> <specifiedPickupDirectory pickupDirectoryLocation="C:\mail"/> <network host="localhost"/> </smtp> The transformation: <?xml version="1.0"?> <smtp xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xdt:Transform="Replace" from="[email protected]" deliveryMethod="Network"> <network .../> </smtp>Inoue
I'm not sure I understand. In this configuration I don't transform anything, it's just copying files...Engrave
Oh, I didn't see the copy part. I transform the config instead of just copying it. Thanks anyway.Inoue
I like this solution. One small suggestion: In the copy example above the source and target arguments for copy should be surrounded by quotes; otherwise Pre-Build will fail for directories with space in their nameMemphis
S
30

You can use a separate config file per configuration, e.g. app.Debug.config, app.Release.config and then use the configuration variable in your project file:

<PropertyGroup>
    <AppConfig>App.$(Configuration).config</AppConfig>
</PropertyGroup>

This will then create the correct ProjectName.exe.config file depending on the configuration you are building in.

Sheaves answered 2/7, 2012 at 9:32 Comment(2)
Thanks, I didn't use your exact example to solve the problem I was having but your example did get me thinking and led me to another very similar soulution using the Copy task.Kronos
Tried this under VS 2015 Community RC and it builds, but then ignores the content of the app.*.config I have added.Ur
E
14

I wrote nice extension to automate app.config transformation like the one built in Web Application Project Configuration Transform

The biggest advantage of this extension is that you don’t need to install it on all build machines

Excrescent answered 27/5, 2012 at 7:59 Comment(3)
Very useful extension, especially now that Slow Cheetah is entering maintenance mode and may not be supported in the future.Decimal
Yeah, folks should stop going to slow cheetah as the solution for this when this functionality is now supported by the transformxml msbuild task. A sw architect on my team introduced slow cheetah in an overzealous way to our project and created debug, stage, and release transforms of all of our configs, most of which needed no transformation. Needless to say, the moment he left I pulled slow cheetah out and now we just use a single transformxml task on the web.config. Ahhhhh, simplicity. Not to say that slow cheetah didn't have its time and place.Bankroll
slow-cheetah is now maintained by MS github.com/microsoft/slow-cheetah; NuGet contains the old, no-longer-maintained version, and this new version; for full VS integration, also install the appropriate .vsix for your edition from the link.Medarda
V
9

Install "Configuration Transform Tool" in Visual Studio from Marketplace and restart VS. You will be able to see menu preview transform for app.config as well.

https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform

Vierra answered 13/2, 2018 at 17:19 Comment(5)
This works perfectly and requires very little effort or thinking. Much appreciated. thanks. (the 'preview transformation' dosn't work, but the 'add transformations' works perfectly without problem on VS 2017). Also seems to get updates often.Maltose
thank you so much for the solution, behind the scene, it does exactly what Dan Abramov has explained above, without getting your hand dirtyPasha
This is the ultimate solution. The preview seems to work just fine with VS 2019.Heavenly
I love it, but found it did not support other non app.config files without some editing of the csproj. Still great though to see the preview though.Wingfooted
This extension does not seem to work on Visual Studio 2022Taunt
R
5

Just a little improvement to the solution that seems to be posted everywhere now:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  • that is, unless you are planning to stay with your current VS version forever
Revers answered 6/8, 2013 at 8:34 Comment(3)
Can you please explain you answer a little or give sources to explain it?Ulibarri
Doesn't look like $(VisualStudioVersion) is set when using MSBuild directly.Foreign
This should be a comment to https://mcmap.net/q/64339/-app-config-transformation-for-projects-which-are-not-web-projects-in-visual-studio (I've just added the same info as a comment there)Doerrer
C
4

So I ended up taking a slightly different approach. I followed Dan's steps through step 3, but added another file: App.Base.Config. This file contains the configuration settings you want in every generated App.Config. Then I use BeforeBuild (with Yuri's addition to TransformXml) to transform the current configuration with the Base config into the App.config. The build process then uses the transformed App.config as normal. However, one annoyance is you kind of want to exclude the ever-changing App.config from source control afterwards, but the other config files are now dependent upon it.

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="BeforeBuild" Condition="exists('app.$(Configuration).config')">
    <TransformXml Source="App.Base.config" Transform="App.$(Configuration).config" Destination="App.config" />
  </Target>
Carabao answered 29/10, 2014 at 22:5 Comment(0)
E
2

I have created another alternative to the one posted by Vishal Joshi where the requirement to change the build action to Content is removed and also implemented basic support for ClickOnce deployment. I say basic, because I didn't test it thoroughly but it should work in the typical ClickOnce deployment scenario.

The solution consists of a single MSBuild project that once imported to an existent windows application project (*.csproj) extends the build process to contemplate app.config transformation.

You can read a more detailed explanation at Visual Studio App.config XML Transformation and the MSBuild project file can be downloaded from GitHub.

Enrika answered 22/6, 2010 at 15:59 Comment(0)
S
2

If you use a TFS online(Cloud version) and you want to transform the App.Config in a project, you can do the following without installing any extra tools. From VS => Unload the project => Edit project file => Go to the bottom of the file and add the following:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
<TransformXml Source="App.config" Transform="App.$(Configuration).config" Destination="$(OutDir)\$(AssemblyName).dll.config" />

AssemblyFile and Destination works for local use and TFS online(Cloud) server.

Schenck answered 18/3, 2015 at 9:28 Comment(0)
E
1

proposed solution will not work when a class library with config file is referenced from another project (in my case it was Azure worker project library). It will not copy correct transformed file from obj folder into bin\##configuration-name## folder. To make it work with minimal changes, you need to change AfterCompile target to BeforeCompile:

<Target Name="BeforeCompile" Condition="exists('app.$(Configuration).config')">
Erector answered 6/10, 2016 at 18:57 Comment(0)
S
1

Note: Due to reputation I cannot comment on bdeem's post. I'm posting my findings as an answer instead.

Following bdeem's post, I did the following (in order):

1. I modified the [project].csproj file. Added the <Content Include="" /> tags to the ItemGroup for the different config files and made them dependent on the original config file.

Note: Using <None Include="" /> will not work with the transformation.

<!-- App.config Settings -->
<!-- Create App.($Configuration).config files here. -->
<Content Include="App.config" />
<Content Include="App.Debug.config">
  <DependentUpon>App.config</DependentUpon>
</Content>
<Content Include="App.Release.config">
  <DependentUpon>App.config</DependentUpon>
</Content>

2. At the bottom of the [project].csproj file (before the closing </Project> tag), I imported the ${MSBuildToolsPath\Microsoft.CSharp.targets file, added the UsingTask to transform the XML and added the Target to copy the transformed App.config file to the output location.

Note: The Target will also overwrite the App.Config in the local directory to see immediate changes working locally. The Target also uses the Name="Afterbuild" property to ensure the config files can be transformed after the executables are generated. For reasons I do not understand, when using WCF endpoints, if I use Name="CoreCompile", I will get warnings about the service attributes. Name="Afterbuild" resolved this.

  <!-- Task to transform the App.config using the App.($Configuration).config file. -->
  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

  <!-- Only compile the App.config if the App.($Configuration).config file exists. -->
  <!-- Make sure to use the AfterBuild name instead of CoreCompile to avoid first time build errors and WCF endpoint errors. -->
  <Target Name="AfterBuild" Condition="exists('App.$(Configuration).config')">
    <!-- Generate transformed App.config in the intermediate output directory -->    
    <TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />
    
    <!-- Modify the original App.config file with the transformed version. -->
    <TransformXml Source="App.config" Destination="App.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>
</Project>

3. Went back into Visual Studio and reloaded the modified files.

4. Manually added the App.*.config files to the project. This allowed them to group under the original App.config file.

Note: Make sure the App.*.config files have the proper XML structure.

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

<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="myConn" connectionString=""; Initial Catalog=; User ID=; Password=;" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
</configuration>

5. Re-built the project.

Selfabsorption answered 4/11, 2020 at 16:38 Comment(0)
I
1

Yet another variation on @bdeem's answer using Visual Studio 2019 and 2022. My issue was that using that solution, App.config was getting overwritten, and since it's in source control that's not really an option.

The solution for me was to transform the config file directly into the output directory.

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
    <!-- Generate transformed app config to the output directory -->
    <TransformXml Source="App.config" Destination="$(OutDir)\$(TargetFileName).config" Transform="App.$(Configuration).config" />
  </Target>

It has the added benefit of being quite a bit shorter than the original solution.

Instil answered 4/2, 2022 at 17:1 Comment(2)
Can you write where did you put this in csproj ?Kirghiz
@DživoJelić You can see an answer with a little more context here: https://mcmap.net/q/64339/-app-config-transformation-for-projects-which-are-not-web-projects-in-visual-studioInstil
M
0

I am cheating by answering from the future, but slow-cheetah seems like the best solution now. It is currently (start 2024) maintained by Microsoft at https://github.com/microsoft/slow-cheetah.

NuGet contains the original, no-longer-maintained version (also by an MS author), and this new version. The NuGet package is required in your project for build and run support.

For full VS integration (e.g. auto-add transform files on right-click menu option), also install the appropriate .vsix for your edition of Visual Studio, from the link above.

Medarda answered 3/1, 2024 at 10:57 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.