Thursday, July 10, 2008

Development vs. Production Web.config

One of the big pains that I've run into creating a build process for a web site is the web.config.  When running in development, the web.config needs a certain set of development-only settings that get changed for production.  For example connection strings.  When developing, the web.config points to the development database but in production, it should point to the production database.

In the past I've kept two configuration files.  Web.config and Release.config.  At build time, the web.config was overwritten with a copy of the release.config.  Horrible you say?  Yeah, I know.  For those that it's not obvious to, the release.config and web.config will contain a lot of duplicate information.  Especially if you extend the web.config with your own custom configuration sections.  Let the synchronization issues ensue!

The ideal solution to handling the web.config is simply to change those values that you want changed.  But how to do this without writing some kind of script to find and replace these values?

Thankfully, the configuration file is just XML.  XML can be easily modified using XSLT.  And guess what!  NAnt (our build script language of choice) has a <style> step that allows you to perform an XML transformation with XSLT.  Ok, the only thing I needed now was time to get back up to speed on XSLT.  As you may have guessed it, I'm writing this post because I found that time.  Below is the code needed to make those changes.

XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
	<xsl:output method="xml" indent="yes"/>

	<!--Copy every node from the web.config applying templates when possible-->
	<xsl:template match="@* | node()">
		<xsl:copy>
			<xsl:apply-templates select="@* | node()"/>
		</xsl:copy>
	</xsl:template>

	<!--Remove custom debugging app settings-->
	<xsl:template match="/configuration/appSettings/add[@key='CreditCardTestMode']" />

	<!--Remove Conditional Compilation Symbols - Removes the 'compilerOptions' attribute-->
	<!--If you use the DEBUG conditional compilation symbol, you will have to transform your web.config before compiling your website.-->
	<xsl:template match="/configuration/system.codedom/compilers/compiler/@compilerOptions" />

	<!--Remove Debug Trust--> 
	<xsl:template match="/configuration/system.web/compilation/@debug">
		<xsl:attribute name="debug">false</xsl:attribute>
	</xsl:template>

	<!--Switch to production database-->
	<xsl:template match="/configuration/connectionStrings/add[@name='myDB']/@connectionString">
		<xsl:attribute name="connectionString">Data Source=SERVER; Initial Catalog=MYDB; User ID=DBO; Password=REALLYSTRONGPASSWORD;Application Name=APPLICATION;</xsl:attribute>
	</xsl:template>

</xsl:stylesheet>

NAnt Step:

<!--Build website using the aspnet compiler-->
...

<!--Modify web.config for production-->
<style style="${buildfiles.path}webconfig.xslt" in="web.config" out="${website.output.path}web.config" />

One of the other big hurdles is the DEBUG conditional compilation symbol.  Most of our web projects depend on that symbol so things happen a little different while in development (such as exception handling).  Because of this, our web.config would have to be transformed before we compiled.  Thankfully, that code should be able to be removed from the web project and moved into the supporting project.  Yay!  Less code in the web project!  I'm all for less code in the web project.  That's the way it should be.

Technorati Tags: ,,,,,

Submit this story to DotNetKicks

3 comments:

devonianfarm said...

Title should be "How To Build A Rube Goldberg Machine For Configuration Management."

I think it's a bad answer for an important problem.A CM strategy should make it just plain easy to create multiple development, test and production servers: on different machines or all on the same machine.

The "Silver Standard" for managing web site configuration is to keep all of your files in version control except for ONE file with local configuration. There's a mechanism where your Web.Config can include a secondary file... Just install a different secondary file for every installation and you're golden.

A "Gold Standard" approach is to have multiple secondary files, keep them in version control, and have some mechanism to switch which one gets pulled in.

http://gen5.info/q/

SuperJason said...

devonianfarm,

The problem is that you're at high risk for human error. Someone changes one config file, and not another.

The method described works well because it's rare to add "differences" between the configurations.

Obishawn said...

I was actually just yesterday asking if there was a way to extend the web.config but my search kept just turning up information about adding custom configuration sections. Using the phrase "secondary file" in the search helped me find what I was looking for. Thanks devonianfarm.

The method I described works for our situation because there really is only one development environment setup and only one production server and development and QA testing happen in the same environment. It's not the ideal situation, but it's a realistic one for this company. Whenever we can we improve/correct the process, we do. I am definitely going to look into the secondary files now that I know there is a way to do it.

However, I don't believe the way I described is a horrible idea either. The source code is set up for the development environment and build process takes care of preparing it for production.

Thank you again for your comment, devonianfarm.