LINQ to XML, app.config and POCOs

by Administrator 6. November 2010 07:47

I’m a big fan of using the built in configuration mechanism in .NET, that is following the standard approach of putting your configuration settings in an app.config file. By following this approach, you can create a standard configuration mechanism that is well understood by other developers and you get the ability to encrypt sections for free. For those of you that don’t agree that this is the best approach, I recommend you take a look at this Stackoverflow question, which discusses the merits of using the built in configuration mechanism vs. rolling your own.

Way back in .NET 1.1 you could use the IConfigurationSectionHandler interface to enable arbitrary XML to be parsed into any data structures you like- the downside is you had to write code that walked the XML DOM. This was deprecated in .NET 2.0 in favour of creating custom classes, starting with the ConfigurationSection class- the way to leverage these classes is described in this excellent CodeProject article.

Following the standard .NET 2.0 approach is OK, but it requires creating quite a few classes, and the classes that you are supposed to use are written in a .NET 1.1 manner- for example ConfigurationElementCollectiondoes not support generics (although you can fix this), so you end up writing a lot of code for what should be a simple task- mapping XML to C# code. Having used  this approach in the past, it has to be said that it is rather time consuming and you end up with configuration classes that are not POCOs, often use suboptimal data structures and require the use of techniques such as OfType<> to make them play nicely with LINQ. You can of course get round these problems by mapping the configuration classes onto POCOs, but that it turn requires more effort.

I have seen solutions where developers have implemented the deprecated IConfigurationSectionHandler interface and used LINQ to XML to allow the easy mapping of XML to a custom data structure. This is the approach adopted by Steven Sanderson in his excellent book Pro ASP.NET MVC Framework(pg 501 if you own the first edition).

This approach has two flaws- firstly it’s not good using a deprecated interface (although this is likely to be round for a long time, as it is used extensively by the framework itself) and secondly the approach does not work when using an app.config  with a Class Library Project (admittedly the need for doing this is quite rare). After a bit of research and experimentation, I discovered a way that you could create a custom configuration section without having to use a deprecated interface, that also utilized LINQ to XML and allows you to use any data structures you like as well as utilize POCOs.

To demonstrate this approach we create an example .NET 4.0 console application called “NorthdownSolutions.Blog.SampleCode”.

Once Visual Studio has created the Console application project called “NorthdownSolutions.Blog.SampleCode”, add a new app.config, by doing the following:

  1. Right click on the project.
  2. Choose “Add” –> “New Item” from the context menu.
  3. Choose Application Configuration File.

You will now have an app.config in your project. Next modify the XML in the app config file, by adding the following sample XML:

<?xml version="1.0" encoding="utf-8" ?>
<
configuration
>
  <
configSections
>
    <
section
      name="demoSettings"
      type="
NorthdownSolutions.Blog.SampleCode.SampleConfigSection,
            NorthdownSolutions.Blog.SampleCode
"
/>
  </
configSections
>
  <
demoSettings
>
    <
someSettings
>
      <
someSetting value="abc"
/>
      <
someSetting value="def"
/>
    </
someSettings
>
  </
demoSettings
>
</
configuration
>

This XML defines a custom config section called demoSettings. In order to read the values stored in the XML, we need to create a class that subclasses ConfigurationSection.

To do that perform the following steps:

1) Add a reference to System.Configuration.

2) Create a new class called SampleConfigSection that inherits from ConfigurationSection.

3) Override the OnDeserializeUnrecognizedElement method.

For the sake of our example, let’s imagine we wish to store all of the values for the attribute called “value” in a HashSet<string>, which we want to utilize as it allows us to use the Contains method, which is an O(1) operation.

The code to achieve this using LINQ to XML is as follows:

using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

namespace NorthdownSolutions.Blog.SampleCode
{
    public class SampleConfigSection : ConfigurationSection
    {
        public HashSet<string> Values { get; private set; }

        protected override bool OnDeserializeUnrecognizedElement
        (
            string elementName,
            XmlReader reader
        )
        {
            if (elementName == "someSettings")
            {
                XElement xml = XElement.Parse(reader.ReadOuterXml());
                this.Values = new HashSet<string>(
                    xml
                    .Descendants("someSetting")
                    .Select(element => element.Attribute("value").Value));

                return true;
            }

            return false;
        }
    }
}

In our class, we are providing an automatic property that provides the HashSet containing entries “abc” and “cde” (the values provided in the config file). In the OnDeserializeUnrecognizedElement method we look for the XML element we are expecting (someSettings), then we get the XML from the XmlReader, load it into an XElement object and then use LINQ to XML to select every “value” attribute for every “someSetting” element. We pass the returned IEnumerable<string> values into the constructor of a HashSet, completing the mapping of our XML to C# objects.

For safety if OnDeserializeUnrecognizedElement does not pass in an element name of “someSettings” then we return false. This will cause an Exception to be thrown by the configuration API- just the behaviour you want, as it means the configuration file is not correct.

In our sample application, we can then use our HashSet as follows (obviously in the real world you’d be hiding your configuration behind an interface to allow your software to be unit testable):

using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;

namespace NorthdownSolutions.Blog.SampleCode
{
    public class Program
    {
        public static void Main(string[] args)
        {
            SampleConfigSection sampleConfigSection = ConfigurationManager
                .GetSection("demoSettings") as SampleConfigSection;
            HashSet<string> values = sampleConfigSection.Values;

            Debug.Assert(values.Contains("abc"),
                "Checks hashset contains abc");
        }
    }
}

In reality the object represented by the XML is likely to be more complicated than this, but this approach is a great technique for converting arbitrary app.config XML to a POCO C# object graph, without having to implement a deprecated interface.

It also has the added benefit that the same technique can be used regardless of whether the app.config file is for an application or a DLL- in a DLL configuration scenario you have to use the GetSection method of the Configuration class, which returns a ConfigurationSection. This differs the ConfigurationManager’s GetSection method, which allows you to return an object.

Of course you are not limited to using LINQ to XML, you could an XML serialization mechanism to achieve a similar result (albeit a less flexible one). Another good option to investigate would be the configuration section designer project on CodePlex.

Tags:

Comments

11/16/2010 6:29:57 PM #

slurpd

I was looking for something to avoid the use of IConfigurationSectionHandler, and to use LINQ to XML to parse my custom section...
So excellent! Thanks for the trick!!!

slurpd France |