How to add config transformations for a custom config file in Visual Studio?
Asked Answered
C

6

32

The project I am working on involves reading a lot of service endpoints (url) from a config file. Since the list would be quite large I decided to keep them in a custom config file to keep my web.config clean and small. I included the custom section to my web as below:

<mySection configSource="myConfig.config" />

I works perfectly fine.

But the problem of transformation appears during the deployment of the project to different environments. I have three web.config files:

Web.config

Web.Uat.config

Web.Release.config

While the transformation web.config works, the transformations for custom config files fails at deployment.

Is there an way I can transform the custom config file during deployment?

Chambertin answered 12/1, 2016 at 3:31 Comment(1)
Done some digging on this. Got this; Offsite yes. Is it something you looking for; diaryofaninja.com/blog/2011/09/14/…Lagomorph
W
29

Visual Studio transforms only web.config files by default.

If you need custom config file with transformation for DEV, UAT, PROD, etc environments, then try to

  1. Use custom extensions for Visual Studio like SlowCheetah - XML Transforms for Config transformation preview functionality.
  2. Add for the project from Nuget SlowCheetah to provide build in transformation.

A little bit details:

Add VS Extension SlowCheetah from Extensions and Updates Screen of Extensions and Updates

Right click on your myconfig.config and choose add transorm: Screen of Extensions and Updates

Inside each defined configurations insert your own transormation rulles like that:

<services xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <service name="WebApplication1.Services.Service2" xdt:Transform="Replace" xdt:Locator="Match(name)" >
    <endpoint address="http://localhost:57939/Services/DebugService" behaviorConfiguration="WebApplication1.Services.Service2AspNetAjaxBehavior"
      binding="webHttpBinding" contract="WebApplication1.Services.Service2" />
  </service>
</services>
Writ answered 12/1, 2016 at 4:6 Comment(4)
While I reinstalled Visual Studio the answer was downvoted. Hope right now it is okWrit
It solved the issue but I had to add it as a nuget package. The VS extension was able to show "preview transformation" but it was not transforming at the time of publish.Chambertin
There's also an extension on the Visual Studio Gallery called Fast Koala, which has some slight differences, but ultimately achieves the same thing - with the addition that you have the possibility of build time (not publish time) transformations: visualstudiogallery.msdn.microsoft.com/…Addieaddiego
I know this is old, but in case anyone is looking for and doesn't want to install things, I added an answer with another approach that doesn't require installing extensions nor nuget packages.Melosa
D
16

I'm going to extend on Andoni Ripoll Jarauta's answer a little.

We were faced with a similar problem. I wanted to pull the connection strings out of the web.config file to limit merge conflicts. I also wanted create a "release" config containing static information when publishing.

...simple enough. Create a custom config file, webdb.config, and update the web.config file.

Ex. web.config

<connectionStrings configSource="WebDB.config"/>

wedbdb.config (xml version="1.0" is required for transformation)

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

Next add transformation files for webdb.config

enter image description here

WebDB.Debug.config example:

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

<connectionStrings xdt:Transform="Replace" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <add name="PRRADDataContainer" connectionString="metadata=~/PRRADData.csdl|~/PRRADData.ssdl|~/PRRADData.msl;provider=System.Data.SqlClient;provider connection string=';Data Source=localhost;Initial Catalog=;User ID=;Password=;multipleactiveresultsets=True;App=EntityFramework';" providerName="System.Data.EntityClient" />
    <add name="MyConnectionString" connectionString="Data Source=localhost;Initial Catalog=;Persist Security Info=True;User ID=;Password=;" providerName="System.Data.SqlClient" />
</connectionStrings>

WebDB.Release.config example:

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

<connectionStrings xdt:Transform="Replace" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <add name="PRRADDataContainer" connectionString="metadata=~/PRRADData.csdl|~/PRRADData.ssdl|~/PRRADData.msl;provider=System.Data.SqlClient;provider connection string=';Data Source=prod_server;Initial Catalog=;User ID=;Password=;multipleactiveresultsets=True;App=EntityFramework';" providerName="System.Data.EntityClient" />
    <add name="MyConnectionString" connectionString="Data Source=prod_server;Initial Catalog=;Persist Security Info=True;User ID=;Password=;" providerName="System.Data.SqlClient" />
</connectionStrings>

Next we need to add an after-build event. This is created by simply editing the CSPROJ file.

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterBuild">
    <TransformXml Source="WebDB.config" Transform="WebDB.$(Configuration).config" Destination="WebDB.config" />
</Target>

Now when I run locally I'll get WebDB.Debug.config and when I publish my code I just need to make sure to select "Release" as the configuration source. In both cases the WebDB.config file will be updated with the corresponding file when you build.

NOTE: make sure you set the webdb.config, webdb.debug.config, and webdb.release.config to "Do not copy" for the "Copy to Output Directory" option.

Differentiate answered 30/7, 2018 at 19:19 Comment(1)
Couldn't get SlowCheetah's build events to work, but this seems to run the transformations correctly. SlowCheetah does still have the "Preview Transform" option, so one may still want to get it just for that. Also, if you don't get the NuGet Package, it will prompt you for downloading every time. I also didn't have to set "Do not copy."Ause
S
10

I have been using SlowCheetah but I found something that I think is more elegant. Just telling to the build to generate the .config depending on the build configuration.

Having a app.Release.config in your project (or many more depending on you deployment needs) you just need to edit the project file (the .csproj one if you program in C#). Find the end of it, between the last </ItemGroup> and </Project> and add:

  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="AfterBuild">
    <PropertyGroup>
      <OutputTypeName>$(OutputType)</OutputTypeName>
      <OutputTypeName Condition="'$(OutputTypeName)'=='Library'">dll</OutputTypeName>
      <OutputTypeName Condition="'$(OutputTypeName)'=='Module'">dll</OutputTypeName>
      <OutputTypeName Condition="'$(OutputTypeName)'=='Winexe'">exe</OutputTypeName>
    </PropertyGroup>
    <TransformXml Source="Config\app.config" Transform="Config\app.$(Configuration).config" Destination="$(OutputPath)\$(AssemblyName).$(OutputTypeName).config" />
  </Target>
</Project>

Save and reload from VisualStudio. Compile in Release mode and check the bin/Release folder on your <MyProject>.config file the transformation is done.

This example applies to Exe and Dll files and any VisualStudio version because includes this post help

Spinks answered 24/5, 2018 at 9:51 Comment(1)
The solution is also for web.(configration).config?? I am using visual studio 2015 web application.Readership
M
7

There is another approach that doesn't require installing extensions nor using build events.

Let's suppose you have your custom configs like so:

  • myConfig.config
  • myConfig.Uat.config
  • myConfig.Release.config

Then in your main Web.config you have this:

<mySection configSource="myConfig.config" />

Lastly, inside your Web.Uat.config you add a transform like this:

<mySection configSource="myConfig.Uat.config" xdt:Transform="SetAttributes" />

This is not transforming the myConfig.config file, but rather overriding the name of the custom config file that should be used. You can do the same for the Release and any other environments.

Your myConfig.Uat.config should not contain transformations, it should be a copy of the base custom config file, with the appropriate values for the custom environment.

The downside is everytime you add something to the base custom config file, you need to also add to the config files for other envs (even if the value should be the same through envs). So I'd consider just using these custom config files for settings that should be changed between envs.

Melosa answered 28/11, 2018 at 6:35 Comment(0)
K
1

I had a similar need to transform a custom config file, but in a class library. Andoni Ripoll Jarauta's solution worked when I built the project directly, but when I built another project that referenced it the transformed file would not get copied. I found that in addition I had to add the transformed file to AssignTargetPathsDependsOn for that to happen. This did the trick:

<PropertyGroup>
  <AssignTargetPathsDependsOn>
    $(AssignTargetPathsDependsOn);
    BuildCustomConfig;
  </AssignTargetPathsDependsOn>
</PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="BuildCustomConfig">
  <TransformXml Source="MyCustom.config" Transform="MyCustom.$(Configuration).config" Destination="$(OutputPath)\MyCustom.config" />
  <ItemGroup>
    <Content Include="$(OutputPath)\MyCustom.config" Condition="Exists('$(OutputPath)\MyCustom.config')">
      <Link>MyCustom.config</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</Target>
Khaddar answered 29/4, 2021 at 22:45 Comment(0)
C
0

Since the OP asked about Web.config transformations during the deployment lets assume the WPP is already in there. So I've hacked on the WPP.

I use the following snippet to transform Umbraco's own config files (but indeed any configs suit well):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <UmbracoConfigsToTransform Include="Config\umbracoSettings.config">
      <DestinationRelativePath>Config\umbracoSettings.config</DestinationRelativePath>
    </UmbracoConfigsToTransform>
  </ItemGroup>

  <PropertyGroup>
    <CollectWebConfigsToTransformDependsOn>
      $(CollectWebConfigsToTransformDependsOn);
      CollectUmbracoConfigsToTransform
    </CollectWebConfigsToTransformDependsOn>
  </PropertyGroup>

  <Target Name="CollectUmbracoConfigsToTransform">
    <!-- The logic comes from the 'CollectWebConfigsToTransform' task -->
    <ItemGroup>
      <WebConfigsToTransform Include="@(UmbracoConfigsToTransform)">
        <Exclude>false</Exclude>
        <TransformFile>$([System.String]::new($(WebPublishPipelineProjectDirectory)\$([System.IO.Path]::GetDirectoryName($([System.String]::new(%(DestinationRelativePath)))))).TrimEnd('\'))\%(Filename).$(Configuration)%(Extension)</TransformFile>
        <TransformOriginalFolder>$(TransformWebConfigIntermediateLocation)\original</TransformOriginalFolder>
        <TransformFileFolder>$(TransformWebConfigIntermediateLocation)\assist</TransformFileFolder>
        <TransformOutputFile>$(TransformWebConfigIntermediateLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile>
        <TransformScope>$([System.IO.Path]::GetFullPath($(WPPAllFilesInSingleFolder)\%(DestinationRelativePath)))</TransformScope>
      </WebConfigsToTransform>
    </ItemGroup>
  </Target>
</Project>

I name it Umbraco.wpp.targets and drop inside project's root. Then the WPP automatically imports it.

All you have then to do is add a transform file (Config\umbracoSettings.Release.config in case of this sample).

Cruise answered 15/9, 2020 at 12:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.