Monday, July 16, 2012

Web.Config Tips - File vs ConfigSource

Today I would like to share some things I've learned about two useful attributes in web.config: file and configSource. Both of these attributes are invaluable in setting up modular web sites, because both of them let you break up your web.config file into many pieces. I've looked into both the mono and .Net (through reflector) implementations, to see what makes this tick. Here is a summary of what found:

configSource: ConfigSource is built into all ConfigurationSection elements and simply allows you to move a section to an external file. This works for custom sections too, as long as they inherit ConfigurationSection. When using configSource, unfortunately everything must be specified in the external file. Additionally, this didn't seem to play nice with a <?xml /> header, so just keep that line out.
<mySection configSource="newfile.xml" />
<mySection>
  <item value="1" />
  <item value="2" /> 
</mySection>
file: File is only built specifically into the AppSettingsSection. This is a major bummer, since file lets you specify attributes both in the external file, and in web.config itself. If there are conflicts, the external file always wins. This is great for some scenarios - If you have a external file that is replaced in production or QA phase, this lets you ignore web.config changes and only replace the external file.
<appSettings file="newfile.xml" >
  <add key="first" value="true" />
  <add key="second" value="true" />
</appSettings>
<appSettings>
  <add key="second" value="true" />
  <add key="third" value="true" />
</appSetttings>
Unfortuanely, there are two downsides that I've found with the file attribute. First of all, why is this only present in AppSettingsSection? While it doesn't work for all sections, it would be great for the <connectionStrings /> element, as well as many custom sections.

Secondly, if you want to use web.config transforms to separate debug and release builds, this could provide the opposite behavior from what is desired. For example, say a project kept all of it's app settings in a separate file, but for debug builds, you wanted to change some values of the app config, it would be nice to be able to specify the changed appsettings in the web.debug.config transform.

So what if these aren't enough?

If you are creating a custom ConfigSection, your only built-in option is the wholesale-replacement ConfigSource. If you want to mimic functionality provided by "file" (or even create an external file that is overridden by web.config properties), you have one choice:

Build it yourself.

The easiest way to get started is to have a look at the excellent Mono Project, particualry it's implementation of AppSettingsSection. The key method you should look at is DeserializeElement. Unfortunately, Mono seems to have some significant issues in it's implementation of "file". For one, pathing is broken, at least when I use a standard XmlTextReader insted of the mono specific ConfigXmlTextReader. This can be fixed by looking up the file path manually, probably by parsing out from ElementInformation.Source.

Secondly, base.DeserializeElement will be expecting the reader to be positioned inside the wrapper element, so you may need to do a little extra xml reading to get to the first item inside the wrapper. Beyond that, it's up to you how you want your custom external file process to behave.

I'll leave the final implementation as an exercise to the reader, so have fun with it.

2 comments :

  1. Configuration transforms are also a nice thing to throw in the mix. They allow you to add things to existing configuration elements as well.

    You have to graft them on in non-Web apps for some crazy reason, but it can be done.

    Tom (fellow AISer)

    ReplyDelete
  2. Hi.
    I'm working under a task (just out of curiosity, for future projects) to apply Web.config transformation (more generic XSLT than XDT aka Web.config Transform).
    So decided to create a ConfigurationSection intercepting DeserializeElement() and applying transformation before calling 'base' so built-in implementation will know nothing how does actual raw XML look like.
    What for? As you mentioned too - cross-environment (such as QA) configuration modification, e.g. modify connection string.
    So was digging in BCL source code, debugging, all the stuff for several hours - until found that use 'file' attribute instead of 'configSource'. Silly! :)
    But now understood difference between ConfigurationSection and ConfigurationSectionHandler very well.
    Don't you know is XDT applicable for such task? I want to end with the some like this:
    <add name="Bla" connectionString="Foo" env:name="Dev />
    <add name="Bla" connectionString="Foo" env:name="QA />
    So eventually only matching element will remain
    Thanks! Alex

    ReplyDelete