December 2004 - Posts
This was pretty cool, I entered PocketDashboard, my first Compact Framework application, in the "Will Code For Device" contest posted by Thom Robbins (thanks for bringing it to my attention Jamie) and I was chosen as one of the winners. I have no idea what I won, all they said is a shiny new Windows Mobile device...we'll see what shows up. Whatever it is, I probably won't be able to use it since I already have an iPaq I'm quite happy with, and my wireless provider doesn't support smart phones...we'll just have to see. It was fun to enter.
Just wanted to respond to some comments Charles left in response to this post about XmlPreprocess on Jon Galloway's blog... Charles wrote:
Looks like a hack...definitely not the way most natural way of dealing with XML. Using _code_ (okay, "conditional logic") in _comments_ ?!?!
To me, the most natural way of dealing with this situation is to have one config file with all of the data and then have a stylesheet to use to transform the data.
For example, .Net could support Web.config and then Web.Transform.config. The transform would be automatically applied to the Web.config file and the result of the transform would be the file that's actually used by the .Net runtime.
Obviously, the transform would not be necessary for the newbies who just can't handle/don't want to deal with transformations. But for those that need the advanced functionality, learning something as trivial as XSL and XPath would certainly be worth the trouble (and I'm sure most people who need the advanced functionality are probably already well versed in said technologies).
I agree, it certainly could be considered a hack. But I wanted to expand on the reasons I chose to use comments (here's an example:)
<configuration>
<appSettings>
<!-- ifdef ${_xml_preprocess} -->
<!-- <add key="server" value="${remoteserver}"/> -->
<!-- else -->
<add key="server" value="developmentserver1"/>
<!-- endif -->
</appSettings>
</configuration>
Embedding the conditional logic in comments met my goal of keeping the config file 100% operational as-is right out of source control. (Get and Go), and a second goal of having only one definitive source of truth for the structure of the config file (No Dual Maintenance).
I have done configuration using XSL as Charles described, and it worked fine too. But it turns out that we needed one XSL for each config file, at least the way I did it...and in our large applications we have literally dozens of config files scattered all over. So we had double the files to maintain. Plus we have many versions of our application, each targeting it's own set of environments (Dev, Test, Integration, and Production), and the matrix of settings got unmanageable in a hurry.
The stylesheets also turned out to be fragile because they became fairly dependent on the structure of the config file...when the developers adds sections and settings to the config file, you have to go "somewhere else" to update the XSL, it is this out-of-band work that would often get forgotten. There were the developer config files that they kept working, and the XSL's that they didn't really worry about because it didn't affect the way they ran on their machine.
I found the comment ifdef/else/endif syntax very approachable for our developers who didn't know XSL, but were familiar with how simple #ifdef's work.
Consolidating all changeable properties in external settings files also enabled tool support. The XmlPreprocess download includes a sample Excel spreadsheet tool that lets operation personnel edit the properties that change for all environments and in all config files in one single place without having to open a single config file or XSL file. It lists properties down the left, environments across the top. Press a key, and it generates environment specific settings files that get fed into the preprocessor on deployment. Scott Colestock has a screen shot of this tool in this post.
Anyway, I felt my first post, and the project documentation don't explain this very well, hopefully it makes a little more sense now why I chose such a "hack"y looking syntax.
I finally waded through the process of setting up a sourceforge project and uploading content.
This is a simple command line utility that can be used to deploy config files to multiple environments without having to maintain multiple copies. It can be used as a custom action in your MSI to transform the files as they are being deployed to different environments.
http://xmlpreprocess.sourceforge.net/
The following is just pasted in from the overview topic of the users guide to give you an idea of what the tool does.
Overview
This tool can be used to preprocess an XML file and deploy it to a target environment making appropriate environment specific substitutions.
Goals
- Not have to maintain multiple configuration files or templates of configuration files for different environments. In other words, to have a single source of truth.
- Keep the original source of truth configuration file fully operational for development. Developers should be able to get the file directly from source control and run without having to make changes to it.
This is accomplished by decorating the configuration files with non-breaking comments that contain instructions for the preprocessor.
Examples
For example if you want to turn debug page compilation off when in production, you might do something like this:
<configuration>
<system.web>
<!-- ifdef ${production} -->
<!-- <compilation defaultLanguage="c#" debug="false"/> -->
<!-- else -->
<compilation defaultLanguage="c#" debug="true"/>
<!-- endif -->
</system.web>
</configuration>
The debug setting will remain true for an unprocessed file, but when this file is deployed using the XmlPreprocess tool with the "production" property defined, the ifdef condition will be tested, and if true, the comments around its body content will be removed, and the else branch will be omitted entirely. This will render the following:
<configuration>
<system.web>
<compilation defaultLanguage="c#" debug="false"/>
</system.web>
</configuration>
Another powerful way to use the preprocessor is to substitute properties into placeholders in your XML file much like in Ant or NAnt. Properties can be defined in an external XML file or passed on the command line to XmlPreprocess.exe.
For example if you have an application setting that contains the name of a remote server, but the name of that server changes from environment to environment, you may want to mark-up your XML like this:
<configuration>
<appSettings>
<!-- ifdef ${_xml_preprocess} -->
<!-- <add key="server" value="${remoteserver}"/> -->
<!-- else -->
<add key="server" value="developmentserver1"/>
<!-- endif -->
</appSettings>
</configuration>
Then you can provide the value for the "remoteserver" property at deployment time via the commandline or external settings file. For more information see properties.
More Posts