Abusing XAML for Application Configuration

Background

Lately I’ve been working on a small library for easily managing a number of AppDomains.  I’ve been doing a lot of integration testing with WCF and found it quite useful to be able to create, configure and test a bunch of interconnected services in their own AppDomains.  I have the core done, but am still polishing up the rough spots. (Look for a future post on sandboxed integration testing with WCF :) ) But that’s not the point of this post.

I’ve been writing quite a bit of boilerplate code just to get everything configured (which DLLs to copy, what the directory names are, etc). Since this configuration only changes rarely, it would be nice to have it in some sort of XML file instead of hardcoded C#. Naturally I started putting things in App.config.

Anything more complicated than key-value pairs in the appSettings section (which is deprecated now, I think) requires an entire set of classes to interface with the System.Configuration namespace. While certainly doable, it’s not the easiest thing in the world as there are a lot of caveats when it comes to item collections, etc.  In addition, I need the ability to load and save these sandbox configurations on-the-fly while the application is running.  Clearly putting the configuration options in App.config won’t work since I can’t reload or unload it after the application has started.

Basically the configuration that I’m doing is just creating an object tree in memory with the properties set accordingly.  This could be a good candidate for XML serialization. However if I wanted to have Intellisense support in Visual Studio, I’d have to go through and define the XML schema in an XSD file, tell Visual Studio where to find the file, etc.  There are tools to automatically generate a schema from a class, but then they need to be incorporated into the build process…  The simple configuration is starting to get complicated.

Hmmm, but where else have I run across this idea? XML files used to create trees of objects in memory where there is good Intellisense support…Hmmm.

Oh, I know — XAML. XML Application Markup Language. WPF and WF both take advantage of this idea.  Turns out that I can abuse the WPF XAML-parsing machinery to accomplish something for which it was never intended. :)

Sidenote 1: Why didn’t I pick the WF XAML-parsing machinery?  Amazingly, the WPF and WF XAML stacks are completely independent.  Both have the concept of DependencyObjects and DependencyProperties, etc, but are implemented with no overlap.  As it turns out, the WF XAML used in WF is much less functional than the WPF XAML. Why? My guess is that the WF team needed to provide an impenetrable wall between the user of the workflow system and the activity tree (workflow) that is currently executing to avoid corrupting the state of the executing workflow. If you’ve used the WF API for any length of time, this becomes fairly obvious in that you can never actually get a reference to the root activity once the workflow has started executing…  But, I digress :)

Sidenote 2: XAML has become a bit overloaded.  I’ll treat XAML as the general idea of taking an XML file and creating an object tree in memory (as opposed to the more restrictive view that XAML refers specifically to a WPF application file).  WPF is generally associated with .xaml files which use the WPF XAML loader.  Workflows can be written in XOML-only mode (no code-behind) and are associated with .xoml files.

Configuration with XAML

Just as a proof-of-concept, I created two projects — a DLL with the XAML configuration classes, and a console application that loads an XML file and displays its contents.

For the sake of simplicity, I want to have a SandboxConfiguration root element that then contains two properties — FriendlyName and RootDirectory.  In addition, I want the root element to have a sub-element containing 1) a RetrySection with a few knobs to tweak, and 2) A list of SandboxItems.  Each SandboxItem will have two properties — a Path string and a CleanBeforeUse flag.

This is incredibly easy to create — I just make classes with the desired properties:

public class SandboxConfiguration

{

    public string FriendlyName { get; set; }

    public string RootDirectory { get; set; }

 

    public RetrySection RetrySection { get; set; }

 

    private List<SandboxItem> m_SandboxItems = new List<SandboxItem>();

    public List<SandboxItem> SandboxItems { get { return m_SandboxItems; }  }

}

 

public class RetrySection

{

    public int RetryCount { get; set; }

    public int DelayInterval { get; set; }

}

 

public class SandboxItem

{

    public string Path { get; set; }

    public bool CleanBeforeUse { get; set; }

}

This assembly then needs to be marked with an XML namespace so that the XAML loader knows the fully-qualified names of each class.  We just add an XmlnsDefinition attribute in Assembly.cs:

[assembly: XmlnsDefinition("http://thevalerios.net/matt/XamlConfig", "XamlConfig")]

This just creates a mapping between the XML namespace (http://thevalerios.net/matt/XamlConfig) and the .NET namespace (XamlConfig).

Sidenote 3: The XML Namespace doesn’t have to be a URI.  It’s also optional (but requires a different syntax in the XAML — see Sidenote 4)

As a test, I created another console application containing a config.xaml file.  I could have just made this a regular .xml file extension, but I wanted to take advantage of Visual Studio’s advanced Intellisense for XAML files.

Here is the contents of config.xaml:

<?xml version="1.0" encoding="utf-8" ?>

<SandboxConfiguration

    xmlns="http://thevalerios.net/matt/XamlConfig"

    FriendlyName="SandboxGroup1">

    <SandboxConfiguration.RootDirectory>

        C:\Sandbox\Test1\

    </SandboxConfiguration.RootDirectory>

    <SandboxConfiguration.RetrySection>

        <RetrySection RetryCount="10" DelayInterval="5"/>

    </SandboxConfiguration.RetrySection>

    <SandboxConfiguration.SandboxItems>

        <SandboxItem CleanBeforeUse="True" Path="C:\Temp\"/>

        <SandboxItem CleanBeforeUse="False" Path="C:\Temp2\"/>

        <SandboxItem CleanBeforeUse="True" Path="C:\Temp3\"/>

    </SandboxConfiguration.SandboxItems>

</SandboxConfiguration>

Note that the xmlns attribute matches the XML namespace that I marked the first assembly with.

Sidenote 4: If you want to be able to have fine-grained control over exactly where the types are loaded from (e.g. different versions of the same assembly), you can use an alternate format for the xmlns attribute.  Specifically, you can give the fully-qualified namespace of the types as well as the assembly in which those types are found, including the version number, etc.  To do this, you’d have a line that would look like this: xmlns="clr-namespace:Name.Space;assembly=Assembly.Name,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null".  Notice that there are no spaces and pay careful attention to the location of semicolons and commas!

Also note that both properties of the SandboxConfiguration class (FriendlyName and RootDirectory) are just regular properties but are being set differently.  XAML lets you set simple properties (like strings or ints) using the attribute syntax (e.g. FriendlyName), but you can also assign properties using the "attached property" syntax (e.g. a full XML element named SandboxConfiguration.RootDirectory).

The "attached property" syntax is used again for the RetrySection property, creating a new RetrySection object (with RetryCount=10 and DelayInterval=5) and assigning it to the RetrySection property.

For the SandboxItems property, this illustrates how to handle collections — just keep tacking on SandboxItems as needed.

Once all of this is set up, creating the object tree in memory from config.xaml is incredibly painless using the XamlReader.Load method:

SandboxConfiguration config = (SandboxConfiguration)XamlReader.Load(File.OpenRead("config.xaml"));

Cool! For this particular application, this approach seems to work well.

Caveats

The assembly where the configuration classes live (XamlConfig in this case) needs to reference the WindowsBase assembly to get the XmlnsDefinition attribute. The assembly that is using the XamlReader must reference the WindowsBase, PresentationBase, and PresentationFoundation assemblies. It seems rather overkill that I’m using the entire WPF subsystem just to load an XML file.

I also haven’t done an extensive (and fair) comparison between XAML and the System.Configuration namespace.  There are a few things that the System.Configuration namespace can do that XAML can’t, such as inheritance, overriding settings between multiple files (App.config, Machine.config), etc.

Conclusion

I’ve been able to show a simple example illustrating that it’s possible to use the WPF XAML functionality in the .NET framework to do things completely unrelated to graphical interfaces. The Intellisense support in Visual Studio is first-class and doesn’t require an external schema to be defined. It’s certainly possible to use XAML for loading configuration data and works well if the configuration is an object tree.

Also, just because something is possible doesn’t make it a good idea :) I’m going to experiment with it a little more and see if I run into any bumps.

Hope someone finds this helpful! :)

Edit: Apparently I’m not the first to bump into using XAML for configuration.  Daniel Cazzulino has a good post about why he thinks XAML will make System.Configuration and Enterprise Library Configuration obsolete. In it, he references this page by Martin Fowler about DSLs (domain-specific languages). Great stuff. I’m not 100% convinced that it’s a drop-in replacement (yet), though…


You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

AddThis Social Bookmark Button

Leave a Reply