Dynamic App.config key property mapping

Written by Lawrence Botley

An elegant and simple approach to dynamic property/App.config key mapping in C#

Mapping properties to app.config keys is a great way to avoid hardcoded "magic strings" and configuring parameters in your application without the need to recompile. There are however a few annoying steps that are needed in order to get your parameters loaded into the property itself whilst ensuring that the referenced keys are always present in config files. Its also an added headache if you also need to peform type conversions.

Traditionally, you would want to map your properties to the config file on each of the properties getters itself, and this is fine, however it looks a little messy, it’s a little harder to maintain and since its lazy loading, any mis-mapped properties won’t have an exception thrown until they are needed. In the case of missing key values with nullable primitive types such as strings your calling code may not even throw an exception at all, or even throw and exception in different parts of your application.

Traditional app.config mapping with type conversion

Your app.config will look something like this

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="WaitFor" value="1000" />
  </appSettings>
</configuration>

And your c# property will look something like this

private static int? waitFor;

public static int? WaitFor
{
	get
	{
		if(waitFor==null)
		{
			var value = ConfigurationSettings.AppSettings["WaitFor"];
			var typeConverter = TypeDescriptor.GetConverter(typeof(int));
			var convertedValue = (int?)typeConverter.ConvertFromString(value);
			waitFor = convertedValue;
		}

		return waitFor;
	}
}

This works just fine but it seems a bit excessive to merely load a value especially if you have 5 of these in your class. It also feels a little messy to have a private field member variable for each property and can also make it difficult to determine if a property has been sucessfully loaded if the type is non-nullable (such as integer values). Finally, we have gotten used to auto-implemented properties since C# 4.0 so using field variables is just plain rude! ;)

It would be a much cleaner and a more manageable approach to simple decorate your property with a custom attribute and let all the load mapping happen behind scenes. So, introducing the Eager loading app-setting property attribute (yes, quite a descriptive mouthful).

        [AppSettingProperty(Name = "GlobalUserId")]
        private static string UserId { get; set; }

When the application fires up you should place a single call to eager load the properties from the app.config file.

    class Program
    {
        static void Main(string[] args)
        {
            // Preload the app.config keys
            AppSettingProperty.EagarLoadAppSettings();
        }
    }

The EagarLoadAppSettings() method uses reflection to determine all the properties in the assembly that are decorated with the AppSettingProperty attribute. Afterwhich, it will check for presents of the referenced key in the app.config xml (throwing an exception in the event of its absence) and performing any type conversions on the fly. You can also notice that the property name doesn’t have to match the App.config key name and a different key name can be specified using the Name property. This is to assist with naming conventions and multiple keynames that may conflict such as Id properties within a class

The nuts and bolts

/// <summary>
/// Reflects all the properties with the CachableAppSetting and loads them from the app.config
/// </summary>
public static void EagarLoadAppSettings()
{
	foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
	{
		// get all of the properties in the type with the CachableAppSetting attribute
		var propertyInfoList =
			type.GetProperties(BindingFlags.Static | BindingFlags.NonPublic).Where(
				pi => pi.GetCustomAttributes(typeof(AppSettingProperty), false).Any());

		foreach (var propertyInfo in propertyInfoList)
		{
			var appSettingProperty =
				(AppSettingProperty)propertyInfo.GetCustomAttributes(typeof(AppSettingProperty), true).First();
			var keyName = appSettingProperty.Name ?? propertyInfo.Name;

			var value = ConfigurationSettings.AppSettings[keyName];
			if (value == null)
			{
				throw new Exception(string.Format("Expected App key {0} for class {1}", keyName, type.Name));
			}

			var typeConverter = TypeDescriptor.GetConverter(propertyInfo.PropertyType);
			var convertedValue = typeConverter.ConvertFromString(value);
			propertyInfo.SetValue(null, convertedValue, null);
		}
	}
}

You can download the working example which gives you everything you need to run the dynamic property mapper

© 2016 RK Consulting. All rights reserved.