Expressive Configuration Files with Evaluators

I’ve not played around too much with WPF, but one of the things I do like about it, is the expressive binding syntax you can use to declaratively apply binding to elements, e.g:

  <ListBox Name="entryListBox" ItemsSource="{Binding Source={StaticResource RssFeed}, XPath=//item}" />

It lead me to think, could we provide something similar, but apply it to something such as a configuration file, or really any string content? Here is what I am aiming to achieve:

  <configuration>
    <appSettings>
      <!-- Simple Values -->
      <add key="MyFirstSetting" value="First" />
      <add key="MySecondSetting" value="{AppSetting MyFirstSetting}" />
      
      <!-- A connection string lookup -->
      <add key="DefaultDatabaseConnection" value="Test" />
      
      <!-- Other settings -->
      <add key="DatabaseServer" value="192.168.0.1,1724" />
      
      <add key="Database" value="Test" />
      <add key="Database_Staging" value="Staging" />
      <add key="Database_Production" value="Production" />
      
      <add key="MachineType" value="Staging" />
      
      <!-- Composite expressions -->
      <add key="SomeSetting" value="MySecondSetting" />
      <add key="SomeCompositeSetting" value="{AppSetting {AppSetting SomeSetting}}" />
    </appSettings>
    
    <connectionStrings>
      <add name="Test" connectionString="Server=(local);Database=Test;Integrated Security=true;" />
      <add name="Main" connectionString="Server={AppSetting DatabaseServer};Database={AppSetting Database};Integrated Security=true" />
    </connectionStrings>
  </configuration>

The idea being that we could evaluate these expressions at runtime without having to worry about composing variables from say, the Application settings, or connection strings, etc.

The way I approached this, was to define a interface, the IEvaluator. This would allow me from the start to make the mechanism extensible. The interface looks like this:

public interface IEvaluator
{
    #region Properties
    string Name { get; }
    #endregion

    #region Methods
    string Evaluate(string value);
    #endregion
}

It’s a simple interface, promoting a name (which will directly correlate to the left-hand value, e.g. “AppSetting” in “{AppSetting MyFirstSetting}”), and the method which will evaluate the actual argument itself. Here is an example evaluator, the AppSettingEvaluator:

public class AppSettingEvaluator : EvaluatorBase
{
    #region Properties
    public override string Name { get { return "AppSetting"; } }
    #endregion

    #region Methods
    protected override string EvaluateCore(string value)
    {
        return From.AppSetting(value);
    }
    #endregion
}

I’ve implemented a base class which handles the caching of any results, and this method simply uses a From.AppSetting(…) method call which is a simple utility function. The evaluator mechanism itself is driven through a regular expression, that will match a pattern like “{<key> <value>}”, and allow us to extract those values and replace. We also provide a list of evaulators (bundling our AppSettingEvaluator and ConnectionStringEvaluator as standard, but it does provide a mechanism to register your own:

public static class Evaluator
{
    #region Fields
    private static readonly Regex ExpressionRegex = new Regex(
        "\{(?<key>[a-z]*?)\s(?<value>[^{^}]*?)\}",
        RegexOptions.IgnoreCase
        | RegexOptions.Singleline
        | RegexOptions.CultureInvariant
        | RegexOptions.IgnorePatternWhitespace
        | RegexOptions.Compiled
        );

    private static readonly IList<IEvaluator> _evaluators = new List<IEvaluator>()
    {
        new AppSettingEvaluator(),
        new ConnectionStringEvaluator()
    };
    #endregion

    #region Methods
    public static void AddEvaluator(IEvaluator evaluator)
    {
        if (evaluator == null)
            throw new ArgumentNullException("evaluator");

        _evaluators.Add(evaluator);
    }

    public static string Evaluate(string value)
    {
        if (string.IsNullOrEmpty(value))
            return value;

        if (!HasExpression(value))
            return value;

        value = ExpressionRegex.Replace(value, m =>
        {
            string key = m.Groups["key"].Value;
            string val = m.Groups["value"].Value;

            var evaluator = _evaluators.Where(e => key.Equals(e.Name)).FirstOrDefault();
            if (evaluator == null)
                return string.Format("[{0} {1}]", key, val);

            return evaluator.Evaluate(val);
        });

        return Evaluate(value);
    }

    public static bool HasExpression(string @string)
    {
        if (string.IsNullOrWhiteSpace(@string))
            return false;

        return ExpressionRegex.IsMatch(@string);
    }
    #endregion
}

The bulk of the work is done by the static Evaluate method, which uses our regular expression, and applies replacements based on the keys found. If we don’t have an evaluator, we re-write the values with square braces “[<key> <value>]” to highlight that it was not matched, and also to safeguard from infinite loops. We use this method recursively to ensure that we can support composite expressions, e.g. “{ConnectionString {AppSetting DefaultDatabaseConnection}}”.

Automatically Evaluating in Custom Configuration Sections

The next logic step for me, was to see how we could automatically apply these expression evaluations to custom configuration sections. Given that configuration is read once and cached for the lifetime of the application, the initial hit would be the first time the configuration is read. Although we can’t make modifications to the stable set of configuration sections, for appSettings, connectionStrings etc, we could potentially provide custom evaluation for our own configuration sections. The way we do this, is we need to subclass ConfigurationSection, and intercept the xml being read before it is processed. To do this, we need to override the DeserializeElement(…) method of the ConfigurationElement type. We do this at the ConfigurationSection stage, as it will apply the change to the entire configuration section, not individual elements. Here is how it looks:

public abstract class EvaluatedConfigurationSectionBase : ConfigurationSection
{
    #region Methods
    protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
    {
        string content = reader.ReadOuterXml();
        content = Evaluator.Evaluate(content);

        var manager = new XmlNamespaceManager(reader.NameTable);
        var context = new XmlParserContext(reader.NameTable, manager, null, reader.XmlSpace);
        var newReader = new XmlTextReader(content, XmlNodeType.Element, context);

        newReader.Read();
        base.DeserializeElement(newReader, serializeCollectionKey);
    }
    #endregion
}

With that, we can implement a custom configuration section:

public class TestConfigurationSection : EvaluatedConfigurationSectionBase
{
    #region Fields
    private const string TestPropertyAttribute = "testProperty";
    private const string TestElementElement = "testElement";
    #endregion

    #region Properties
    [ConfigurationProperty(TestPropertyAttribute, IsRequired = true)]
    public string TestProperty
    {
        get { return (string)this[TestPropertyAttribute]; }
    }

    [ConfigurationProperty(TestElementElement, IsRequired = false)]
    public TestConfigurationElement TestElement
    {
        get { return (TestConfigurationElement)this[TestElementElement]; }
    }
    #endregion
}

There is nothing special about this class, its standard implementation of a custom configuration section. We haven’t had to mark anything to be evaluated, it’s just handled for us automatically. Here is what it would look like in config:

<test testProperty="{ConnectionString {AppSetting DefaultDatabaseConnection}}">
  <testElement testProperty="{ConnectionString Main}" />
</test>

Now, when I read those values:

  var config = (TestConfigurationSection)ConfigurationManager.GetSection("test");

The values of config.TestProperty, and config.TestElement.TestProperty are automatically evaluated for us, just the once. Neat!

Custom Evaluators

Let’s look at a custom evaluator. Historically, I’ve found myself typing Type names out in full multiple times in configuration files (although less and less these days), so I came up with a solution, named types. This is a configuration section that represents a mapping between a shortname and the full type name, e.g.:

<namedTypes>
  <add name="String" type="System.String" />
  <add name="CompositionContainerFactory" type="My.Namespace.For.A.CompositionContainerFactory, My.Assembly" />
</namedTypes>

We’ll skip over the configuration section implementation, and jump to the evaluator:

public class NamedTypeEvaluator : EvaluatorBase
{
    #region Properties
    public override string Name { get { return "NamedType"; } }
    #endregion

    #region Methods
    protected override string EvaluateCore(string value)
    {
        return NamedTypes.GetNamedType(value);
    }
    #endregion
}

We can register this evaluator:

Evaluator.AddEvaluator(new NamedTypeEvaluator());

And assuming we use it somewhere, e.g., in an application setting:

<appSettings>
  <!-- Container Factory Type -->
  <add key="ContainerFactory" value="{NamedType CompositionContainerFactory}" />
</appSettings>

This value can automatically be evaluated for us. I think the obvious next step is to compose evaluators from your inversion of control (IoC) container or service locator, so you introduce a dynamic plug and play system.

The project is attached, let me know your thoughts.

Expressive Configuration files with Evaluators

Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)