On this page

Skip to content

Applications of Web.config (App.config) in .NET Framework

Publishing Different Configuration Files Based on Build Configurations

Normally, Web.config (App.config) settings differ between local development, testing, and production environments. It is cumbersome to manually update settings every time the application is updated. Furthermore, if you choose not to update the Web.config (App.config) during deployment, you risk forgetting to adjust settings in the test/development environments when new configurations are added or modified.

One approach is to overwrite the configuration file with every update, but to generate the environment-specific configuration file during the deployment process.

Build Configurations

If you need to build a project based on different settings, you will need to configure Build Configurations. Different configurations cover generating different configuration files, compiler options, or producing different compilation results based on project constants. For details, please refer to "Understand build configurations".

The default build configurations are debug and release, which can be switched in the IDE.

vs build configuration menu

Web Project Publishing

Regarding project publishing, this article only covers folder publishing. I have not explored other publishing methods; if needed, please refer to "Quickstart: Deploy an ASP.NET web app".

Adding a Publish Profile

  1. Right-click the project and select "Publish".

    vs project publish menu

  2. Click "Add a publish profile".

    vs publish new profile

  3. This article only discusses folder publishing, so select "Folder".

    vs publish target folder

  4. Set the output folder. I usually set it to bin\Publish\{ConfigurationName}. Here, I set it to bin\Publish\Release.

    vs publish location setting

  5. After adding, it is recommended to rename the publish profile to match the configuration name or provide a brief description.

    vs publish profile rename

  6. Edit the publish settings. Basic settings can be edited via the UI. I check all boxes here, but in practice, please adjust according to your needs. For example, if you are publishing to a website and set it to "Delete all existing files prior to publish," it will cause temporary files stored on the site (e.g., uploaded files) to be deleted as well.

    vs publish profile settings

  7. You can directly modify the XML to change publish settings, for example, by adding <ExcludeFilesFromDeployment>packages.config;Scripts\_references.js</ExcludeFilesFromDeployment> to exclude specific files during deployment. As for the specific available XML publish settings, I have not found relevant documentation on MSDN.

    vs publish profile preview

TIP

When a publish profile is created, a file named "{Profile}.pubxml.user" is also generated. The purpose of this file is unclear; it looks more like a publish log, which differs from the MSDN description. In any case, this file should not be included in version control. In fact, the default Visual Studio ".gitignore" ignores user setting files with the ".user" extension.

Web.config Transform

When creating a new Web project, you will find three default config files in the project root: "Web.config", "Web.Debug.config", and "Web.Release.config". However, when building the project, you will notice that it always uses the "Web.config" settings. The other two configs are actually used to transform parts of "Web.config" during publishing.

  • If you find that the "Web.{ConfigurationName}.config" file corresponding to a build configuration is missing (perhaps due to accidental deletion or creating a new build configuration), you can right-click on "Web.config" and select "Add Config Transform" to create the missing config file.

vs add config transform

  • Right-click on "Web.{ConfigurationName}.config" and select "Preview Transform" to view the transformation results.

vs preview transform

  • Transform replacement mechanism. Transform uses XML structure to locate content to be replaced. The xdt:Transform attribute sets the replacement method. Let's use the following example for explanation:

    xml
    <a>
      <b>
        <c>
          <e name="e1" value="v1"></e>
          <e name="e2" value="v2"></e>
        </c>
        <d></d>
      </b>
    </a>

    If you want to replace the content of the <d> node, you need to prepare an XML that includes the <d> node and all its parent nodes, as shown below:

    xml
    <a>
      <b>
        <d xdt:Transform="Replace"></d>
      </b>
    </a>

    For nodes like <e> where there are multiple nodes with the same name under the same parent, you need to use the xdt:Locator attribute to locate them. For example, if you want to replace <e name="e1"></e>, the XML content would be:

    xml
    <a>
      <b>
        <c>
          <e name="e1" value="v11" xdt:Locator="Match(name)"
              xdt:Transform="SetAttributes"></e>
        </c>
      </b>
    </a>
  • Common transformation settings.

    • Replacing connectionStrings.

      xml
      <!--Replace all connectionStrings-->
      <connectionStrings xdt:Transform="Replace">
        <add name="Default" connectionString="{connectionString}"
             providerName="System.Data.SqlClient" />
      </connectionStrings>
      
      <!--Replace only a single string-->
      <add name="Default" connectionString="{connectionString}"
           providerName="System.Data.SqlClient"
           xdt:Locator="Match(name)" xdt:Transform="SetAttributes" />
    • Replacing appSettings.

      xml
      <!--It is recommended to replace individually rather than replacing the entire appSettings-->
      <add key="key" value="value1"
           xdt:Locator="Match(key)" xdt:Transform="SetAttributes" />
    • Common system.web replacement items.

      xml
      <!--Change mode="Off" to RemoteOnly and add defaultRedirect -->
      <customErrors mode="RemoteOnly" xdt:Transform="SetAttributes" />
      <!--Change enableVersionHeader to false-->
      <httpRuntime enableVersionHeader="false"
                   xdt:Transform="SetAttributes(enableVersionHeader)" />
      <!--Change requireSSL to true-->
      <httpCookies requireSSL="true"
                   xdt:Transform="SetAttributes(requireSSL)" />
      <!--Remove debug="true" setting-->
      <compilation xdt:Transform="RemoveAttributes(debug)" />

For the complete Transform syntax, please refer to "Web.config Transformation Syntax for Web Project Deployment Using Visual Studio".

Publishing

After selecting publish, you will see the output files in the target folder. At this point, you will find that the generated "Web.config" contains the merged content.

vs publish output

Example

Original Web.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=SQLServer;Initial Catalog=MyDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="MyKey" value="MyValue" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.8.1" />
    <customErrors mode="Off" />
    <httpRuntime targetFramework="4.8.1" />
    <httpCookies requireSSL="false" />
  </system.web>
  <!--Subsequent content unchanged, omitted here-->
</configuration>

Web.Release.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
         xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
    <customErrors mode="RemoteOnly" xdt:Transform="SetAttributes" />
    <httpRuntime enableVersionHeader="false"
                 xdt:Transform="SetAttributes(enableVersionHeader)" />
    <!--Change requireSSL to true-->
    <httpCookies requireSSL="true"
                 xdt:Transform="SetAttributes(requireSSL)" />
  </system.web>
</configuration>

Published Web.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="MyKey" value="MyReleaseValue" />
  </appSettings>
  <system.web>
    <compilation targetFramework="4.8.1" />
    <customErrors mode="RemoteOnly" />
    <httpRuntime targetFramework="4.8.1" enableVersionHeader="false" />
    <httpCookies requireSSL="true" />
  </system.web>
  <runtime>
  <!--Subsequent content unchanged, omitted here-->
</configuration>

Non-Web Project Publishing

Non-Web projects do not support "App.config" Transform by default, so you need to install other packages to handle it.

Packages

  • Visual Studio: Install "SlowCheetah" in Extensions.
  • NuGet: Install "Microsoft.VisualStudio.SlowCheetah" in the project where you want to use Config Transform.

After installing "SlowCheetah" in Visual Studio, an "Add Transform" option will be added to the right-click menu of App.config in non-Web projects, allowing you to generate configs for other configurations.

vs slowcheetah add transform

If "Microsoft.VisualStudio.SlowCheetah" is not installed via NuGet, it will prompt you and install it automatically.

vs slowcheetah nuget prompt

TIP

Actually, Visual Studio has another extension called "ConfigTransformation" that supports Config Transform without requiring an additional NuGet package installation in the project. However, Visual Studio 2022 does not support this extension, and it can only perform transformations on XML files with a full config structure (root node is <configuration />).

Build

Publishing for non-Web ".NET Framework" projects uses ClickOnce and cannot be published to a folder like Web projects. However, its build process triggers Config Transform, so just select the configuration you want to publish and build the project. It is recommended to delete the "bin" folder before building the project.

vs build non web project

Example

Original App.config

xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=SQLServer;Initial Catalog=MyDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyValue" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
  </startup>
</configuration>

App.Release.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
         xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
</configuration>

Built {ProjectName}.exe.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
  </startup>
</configuration>

Sharing Configuration Files Across Different Projects

If an entire solution has multiple projects that need to connect to the same database—such as a website, multiple schedulers, or Web APIs—and you need to modify all projects every time the database environment changes, you might consider placing <connectionStrings /> in a separate file so that multiple projects can reference it.

Setup Steps

Below, we create a Library project and add three configuration files: "AppGlobal.config", "Connection.config", and "Smtp.config". All three files are configured with Config Transform.

For convenience, these three files will be collectively referred to as external configuration files, and the files generated by Config Transform will be referred to as external configuration settings.

vs solution explorer structure

Setting Config Content

Here, we use AppGlobal.config as an example.

AppGlobal.config

xml
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
  <add key="Test" value="TestValue1" />
</appSettings>
xml
<?xml version="1.0" encoding="utf-8" ?>
<!--The root node must contain the attribute xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" -->
<appSettings xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <add key="Test" value="TestReleaseValue1" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>

Adding References from a Web Project

  • Install "Microsoft.VisualStudio.SlowCheetah" to handle the transformation of these external configuration files.
  • In addition to adding the Library project reference, the Web project needs to add file links to the three new Config files and their related configuration files.
  • Select "Add Existing Item" in the Web project.

vs add existing item menu

Use the "Add As Link" option to add the external configuration files and their related external configuration settings.

vs add as link

  • Open the project file (.csproj) and make the following modifications:
    • Add <TransformOnBuild>true</TransformOnBuild> to indicate that Config Transform should be executed during build.
    • Add <CopyToOutputDirectory>Always</CopyToOutputDirectory> to the external configuration files.
    • Change the external configuration settings from the <Content /> node to the <None /> node to prevent them from being published.
    • Add the <DependentUpon /> node to the external configuration settings so that they are nested under the external configuration file in Visual Studio.
xml
<Content Include="..\BuildConfigSample.Library\AppGlobal.config">
  <Link>AppGlobal.config</Link>
  <TransformOnBuild>true</TransformOnBuild>
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="..\BuildConfigSample.Library\AppGlobal.Debug.config">
  <Link>AppGlobal.Debug.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>
<None Include="..\BuildConfigSample.Library\AppGlobal.Release.config">
  <Link>AppGlobal.Release.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>
<!--Connection.config and Smtp.config are modified the same way-->
  • Modify "Web.config" and "Web.Release.config" to externally reference the external configuration files.
    • The difference between configSource and file attributes is that the latter allows adding other settings for merging.

    • <connectionStrings /> and <smtp /> cannot use file.

    • <appSettings /> allows configSource, but file is recommended.

    • If the key in the externally referenced file is the same as a key added in <appSettings />, the externally referenced value will be used, so you cannot override settings by defining the same key.

      • Using the link method to add configuration files means there are no actual files in the project root, causing "Web.config" to fail to find the external configuration files in the same folder when running in Debug mode. Therefore, reference the files under "bin" (we set the external configuration files to "Copy to Output Directory" earlier).

      Web.config

      xml
      <configuration>
        <connectionStrings configSource="bin\Connection.config" />
        <appSettings file="bin\AppGlobal.config">
          <add key="webpages:Version" value="3.0.0.0" />
          <add key="webpages:Enabled" value="false" />
          <add key="ClientValidationEnabled" value="true" />
          <add key="UnobtrusiveJavaScriptEnabled" value="true" />
          <add key="MyKey" value="MyValue" />
        </appSettings>
        <system.net>
          <mailSettings>
            <smtp configSource="bin\Smtp.config" />
          </mailSettings>
        </system.net>
        <!--Subsequent content omitted-->
      </configuration>

      Web.Release.config

      xml
      <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
        <connectionStrings configSource="Connection.config" xdt:Transform="SetAttributes" />
        <appSettings file="AppGlobal.config" xdt:Transform="SetAttributes">
          <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
        </appSettings>
        <system.net>
          <mailSettings>
            <smtp configSource="Smtp.config" xdt:Transform="Replace" />
          </mailSettings>
        </system.net>
        <!--Subsequent content omitted-->
      </configuration>

Adding References from a Non-Web Project

The steps are the same as for Web projects, but the modifications to the project file and App.config are slightly different.

The external configuration files in the project file use the <None /> node.

xml
<None Include="..\BuildConfigSample.Library\AppGlobal.config">
  <Link>AppGlobal.config</Link>
  <TransformOnBuild>true</TransformOnBuild>
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="..\BuildConfigSample.Library\AppGlobal.Debug.config">
  <Link>AppGlobal.Debug.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>
<None Include="..\BuildConfigSample.Library\AppGlobal.Release.config">
  <Link>AppGlobal.Release.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>

TIP

The <None /> node and <Content /> correspond to the build actions "None" and "Content" respectively. "Content" is for Web use. For details, please refer to "Build action values".

App.config

In Debug mode, the configuration file itself uses the "{ProjectName}.exe.config" file under "bin{ConfigurationName}", so the path does not need to be specified under "bin".

xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings configSource="Connection.config" />
  <appSettings file="AppGlobal.config">
    <add key="MyKey" value="MyValue" />
  </appSettings>
  <system.net>
    <mailSettings>
      <smtp configSource="Smtp.config" />
    </mailSettings>
  </system.net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
  </startup>
</configuration>
App.Release.config

After switching to external references, App.Release.config no longer needs to transform the connectionStrings content.

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
</configuration>

WARNING

If you are using Entity Framework's Database First, the connection string in the project's App.config cannot be set using an external reference file; otherwise, the Entity Framework UI will not be able to find the configured connection string.

Breaking the Inheritance of Web.config

IIS allows building sub-websites under a website. In this case, the sub-website's Web.config will inherit the parent website's settings, which can sometimes cause conflicts, especially if there are differences in the Framework versions between the two. You can use <location> in the parent website to break the inheritance as follows:

xml
<location path="." inheritInChildApplications="false">
  <system.web>
    <!--...Internal code...-->
  </system.web>
</location>

TIP

Theoretically, writing <location /> outside of <system.web /> is sufficient, but if necessary, it can also be added outside other nodes. However, if the project uses Entity Framework's Database First, do not write it outside the <connectionStrings /> layer, otherwise, it will cause the Entity Framework UI to be unable to read the configured connection string (this problem again, orz).

Change Log

  • 2022-11-10 Initial version created.