Introduction
In all but the smallest companies, applications are frequently moved from various environments such as: Development, QA and, finally, Production. Particularly large organizations may even have User Acceptance and/or Staging environments. As applications move through these environments, elements of the application's config file such as connection strings, server names, or Web URLs must also change. This article develops a system in which data in the config file can be automatically changed to match the target environment of the application.
Background
I have seen developers use various methods to organize config files; all of which get the job done while introducing new problems at the same time. Two popular methods are given below.
Commented Sections
In this method, the application's config file contains duplicated entries for each environment and the proper sections are left uncommented while others are wrapped in comments. This method has three downsides: one is that the config file can grow quite large due to repeated text. Secondly, developers must ensure that changes are properly replicated to each duplicated section. Finally, unless an automated build tool parses the config file, developers must ensure the config file is left in a proper state for promotion to the next environment.
Multiple Config Files
In this method, a config file is created for each environment often with names like dev.config, prod.config, etc. These files are then renamed to app.config or web.config as needed. Again, unless an automated build tool renames the config file, developers must do this manually; and, because each file contains a copy of common config entries, you must ensure that changes are properly replicated to each file.
The method presented here moves configuration information common to all environments into a separate file which is then merged with environment specific information via an XSL transformation. When done as a pre-build step, this process will automatically create a config file for the proper environment.
Using the Code
The first step is to create an XML file that will contain the common configuration settings. This file will likely have an empty connectionstrings
section and the appSettings
that are common to all environments. An example of such a file named PreConfig.xml is shown below.
="1.0"="utf-8"
<configuration>
<configSections>
</configSections>
<connectionStrings>
</connectionStrings>
<appSettings>
<add key="FormatString" value="{0} {1}"/>
</appSettings>
</configuration>
The next step involves creating an XSLT transformation file for each environment. We will name these files matching the Configuration
names we have in Visual Studio. In this example, we create a Debug and a Release file. Shown below is the transformation file Debug.xslt for the Debug environment.
="1.0"="UTF-8"
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="utf-8" />
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="connectionStrings">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
<add name="Northwind" connectionString="Data Source=localhost;
Initial Catalog=Northwind;Integrated Security=True"
providerName="System.Data.SqlClient" />
</xsl:copy>
</xsl:template>
<xsl:template match="appSettings">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
<add key="Salutation" value="Greetings from the DEBUG environment"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
We also create a transformation file Release.xslt for the Release environment.
="1.0"="UTF-8"
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="utf-8" />
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="connectionStrings">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
<add name="Northwind" connectionString="Data Source=ProdServer;
Initial Catalog=Northwind;Integrated Security=True"
providerName="System.Data.SqlClient" />
</xsl:copy>
</xsl:template>
<xsl:template match="appSettings">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
<add key="Salutation" value="Greetings from the RELEASE environment"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And finally, you will need a Pre-Build Event command similar to the following:
cd $(ProjectDir)
msxsl Preconfig.xml $(ConfigurationName).xslt -o App.config
These commands assume that you are using Microsoft's Command Line Transformation Utility msxsl.exe. For the demonstration project, the utility was included in the project folder. If you have the tool (or a different utility) at a different location, change the pre-build event command as needed.
Points of Interest
If you leave the App.config file open in Visual Studio, you will get the "File has been modified outside the source editor" dialog with each build. This can get annoying and is easily avoided by not leaving the file open!
History
- 4th June, 2008: Original article