Expressive Configuration Files with Evaluators

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}" />
Code language: HTML, XML (xml)

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:

<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=",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> <configuration>
Code language: HTML, XML (xml)

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 }
Code language: PHP (php)

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 }
Code language: PHP (php)

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:

<!-- wp:preformatted --> <pre class="wp-block-preformatted">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 } </pre> <!-- /wp:preformatted --> <!-- wp:paragraph --> <p>With that, we can implement a custom configuration section:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">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 } </pre> <!-- /wp:preformatted --> <!-- wp:paragraph --> <p>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:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">&lt;test testProperty="{ConnectionString {AppSetting DefaultDatabaseConnection}}"&gt; &lt;testElement testProperty="{ConnectionString Main}" /&gt; &lt;/test&gt; </pre> <!-- /wp:preformatted -->
Code language: HTML, XML (xml)

Now, when I read those values:

var config = (TestConfigurationSection)ConfigurationManager.GetSection("test");
Code language: JavaScript (javascript)

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>
Code language: HTML, XML (xml)

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());
Code language: JavaScript (javascript)

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

<appSettings> <!-- Container Factory Type --> <add key="ContainerFactory" value="{NamedType CompositionContainerFactory}" /> </appSettings>
Code language: HTML, XML (xml)

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

Leave a Reply

Your email address will not be published.

Related Post