Jay Harris is Cpt. LoadTest

a .net developers blog on improving user experience of humans and coders
Home | About | Speaking | Contact | Archives | RSS
Filed under: NAnt | Task Automation | Tools

Scott Hanselman posted an entry yesterday about Managing Multiple Configuration File Environments with Pre-build Events. His design uses pre-build events in Visual Studio to copy specific configuration files to the default file name, such as having "web.config.debug" and a "web.config.release" configuration files and the pre-build copying the appropriate file to "web.config" based on which build configuration you are in. This is a great idea, but large web.config files can get tedious to maintain, and there is a lot of repeated code. Even using include files as Scott suggests would help, but major blocks, such as Application Settings, may have to be repeated even though only the values change. This exposes human error, since an app setting may be forgotten or misspelled in one of your web.config versions.

At Latitude, we manage this problem through NAnt. One of our former developers, Erik Nelsestuen–brilliant guy–authored the original version of what we call "ConfigMerge". Essentially, our projects have no web.config under source control. Instead, we have a web.format.config. The format config is nearly identical to the web.config, except all of the application settings and connection strings have been replaced with NAnt property strings. Rather than have a seperate web.config for each environment and build configuration, we simply have NAnt property files. Our build events (as well as our automated build scripts) pass the location of the format file and the location of the property file and the output is a valid web.config, with the NAnt property strings replaced with their values from the environment property file.

It's simple. It only takes one NAnt COPY command.


<project default="configMerge">
  <property name="destinationfile"
    value="web.config" overwrite="false" />
  <property name="propertyfile"
    value="invalid.file" overwrite="false" />
  <property name="sourcefile"
    value="web.format.config" overwrite="false" />
  <include buildfile="${propertyfile}" failonerror="false"
    unless="${string::contains(propertyfile, 'invalid.file')}" />
  <target name="configMerge">
    <copy file="${sourcefile}"
        tofile="${destinationfile}" overwrite="true">
        <expandproperties />

For an example, lets start with a partial web.config, just so you get the idea. I've stripped out most of the goo from a basic web.config, and am left with this:


    <compilation defaultLanguage="c#" debug="true" />
    <customErrors mode="RemoteOnly" /> 

In a debug environment, we may want to enable debugging and turn off custom errors, but in release mode disable debugging and turn on RemoteOnly custom errors. The first thing we will need to do is create a format file, and then convert the values that we want to make dynamic into NAnt property strings.


    <compilation defaultLanguage="c#" debug="${debugValue}" />
    <customErrors mode="${customErrorsValue}" /> 

Next, we need to make NAnt property files, and add in values for each NAnt property that we've created. These property files will include the values to be injected into the web.config output. For the sake of simplicity, I always give my property files a '.property' file extension, but nant will accept any file name; you can use '.foo' if you like.


   <property name="debugValue" value="true" />
   <property name="configMergeValue" value="Off" />


   <property name="debugValue" value="false" />
   <property name="configMergeValue" value="RemoteOnly" />

Finally, we just execute the NAnt script, passing in the appropriate source, destination and property file locations to produce our environment-specific web.config.

nant configMerge -D:sourcefile=web.format.config -D:propertyfile=debugBuild.property -D:destinationfile=web.config
web.config output

    <compilation defaultLanguage="c#" debug="true" />
    <customErrors mode="Off" /> 

And that's all there is to it. This is extendable further using the powers of NAnt. Using NAnt includes, you can put all of your base or default values in one property file that's referenced in your debugBuild.property or releaseBuild.property, further minimizing code. You can use Scott's pre-build event idea to have each build configuration have its own NAnt command to make mode-specific configuration files.

Feel free to use the above NAnt script however you like; but, as always YMMV. Use it at your own risk.


Friday, September 21, 2007 10:00:26 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [5] - Trackback

Filed under: Mush

I need to just make time to blog. I’m finding that I am getting behind, further and further, as the bleeding edge of technology pulls out ahead of me. [sarcasm]Keeping up to date is hard.[/sarcasm].

I am working on a few things to try to get myself back up to speed, beyond just the on-going procrastination over Microsoft .Net 2.0 certification. Last month, I attended Jay Wren’s talk on IoC with Castle at the Ann Arbor .Net Developers Group. Though we’ve been using Castle within our LMS for a few months now, patterns is definitely one of my weak points in developer-land, so I did pick up a few concepts on Dependency Injection. It was a great talk, and I got some swag!

Last week it was back to AADND for Michael Wood’s talk on Windows Workflow Foundation. I haven’t had much any exposure to Windows Workflow Foundation other than to not call it WWF. This stuff blows me away, and makes me even more jealous that we haven’t converted the LMS out of .Net 1.1. I wish I could have made it to the GANG meeting tonight on WF, just to learn more.

Tomorrow is another meeting, and another “Geek Fest” as my wife so lovingly refers to it as. Heading west to MSU for GLUG’s meeting on C# 3.0 by Bill Wagner. There’s going to be a bit on Code Rush, too (I love CodeRush!). Maybe I’ll win more swag!

Wednesday, September 19, 2007 10:10:30 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] - Trackback