Wednesday, May 11, 2011

Strongly typed configuration library for .Net applications


The problem

During my past projects, I were always messing with hard-coded strings when doing application configuration.  
Became more mature in writing maintainable code, I have moved these hard-coded strings to separate constant class (ex. ConstAppConfig). This have helped a bit, but the code itself were filled with custom type conversions (from app.config strings to required types) and tightly coupled with .net configuration classes. 
Later, I have got an idea to create IApplicationConfig interface, which contains all configuration properties.  
The solution with configuration interface have several benefits, over constant class: 
  • Interface contains properties with it's types 
  • Interface make easy stubbing and mocking configuration values in test code  
  • String to Type conversion are implemented in base classes, and not spread across the code  
  • Makes easy to switch between config sources. like app.config, database, test source 
  • Makes easy to do config validation at startup 
  • Base classes provides unified way to threat non-existing or null-value scenarios
  • Facilitates refactoring with automated tools 

Using the code 

Downloading library 

Up to date binaries and source code  is available at Codeplex and Nuget. 

Configuring library   

Library setup and configuration is implemented via ConfigurationServiceBuilder class.
The class has RegisterConfigInterface(param IStringConfigSource[] src) methods, which allows you to register your configuration interface, and define configuration sources. 
Configuration source is a provider of configuration strings, like System.Configuration.ConfigurationManager class. You can use predefined AppConfig source or create your own implementation. 

Using library 

After configuration were finished and method Build were called, you will be provided with instance of ConfigurationService.
This instance holds reference to implementations of all interfaces, that you have registered at configuration phase. Implementations are accessible via  For() method. 
The For() method will give you an implementation of T interface, where all property accessors will do search trough provided configuration sources.

Example code  

Configuration file  
 Collapse
xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="FooCount" value="98"/>
  </appSettings>
</configuration>  
Usage example
 Collapse
using NUnit.Framework;
using Typed.Configuration.ConfigSource;

namespace Typed.Configuration.Tests
{
    [TestFixture]
    public class ConfigServiceUsage
    {
        // Define interface with configuration properties
        public interface IConfigInterface
        {
            [FieldName("FooCount")]
            int? ItemsPerPage { get; }
            
            [FieldName("NotExistingField")]
            int? NotExistingField { get; }
        }

        [Test]
        public void FullUsageScenarioForStubSource()
        {
            var configurationServiceBuilder = new ConfigurationServiceBuilder();

            // Register configuration interface within service
            configurationServiceBuilder.RegisterConfigInterface(new AppSettingsSource(), new NullValueSource());

            // Create service instance and put it to your IoC container, or store in static vairable
            ConfigurationService configService = configurationServiceBuilder.Build();

            // Get configuration property value
            int? itemsPerPageCount = configService.For().ItemsPerPage;
            Assert.AreEqual(98, itemsPerPageCount);
            int? notExistingValue = configService.For().NotExistingField;
            Assert.IsNull(notExistingValue);
        }
    }
}