Convention-based Configuration for Autofac

Here's the configuration file of an application I'm working on:

<?xml version="1.0"?>
    <add key="Repositories.ConnectionString" value="Server=(local)\SQLEXPRESS;Database=XYZ;Trusted_connection=true;"/>

    <add key="Email.Pop3Host" value=""/>
    <add key="Email.Pop3Port" value="995"/>
    <add key="Email.Pop3Ssl" value="True"/>
    <add key="Email.Pop3User" value=""/>
    <add key="Email.Pop3Password" value="password"/>

As an Autofac user, I use modules to structure my application. That means I have modules like:

public class EmailModule : Module
    public string Pop3Host { get; set; }
    public int Pop3Port { get; set; }
    public bool Pop3Ssl { get; set; }
    public string Pop3User { get; set; }
    public string Pop3Password { get; set; }

    protected override void Load(ContainerBuilder builder)
            .WithParameter("PopServer", Pop3Host)
            .WithParameter("Port", Pop3Port)
            .WithParameter("useSSL", Pop3Ssl)
            .WithParameter("Username", Pop3User)
            .WithParameter("Password", Pop3Password);

The convention is that any property in a module can be set by specifying an AppSetting with a key of "Module.PropertyName". That means modules aren't aware of configuration files, and not every component has to be configured in XML (the way Autofac's OOTB XML configuration works).

The ContainerBuilder is built like so:

var modules = new List<Module>();
modules.Add(new RepositoriesModule());
modules.Add(new EmailModule());

var builder = new ContainerBuilder();
builder.RegisterModule(new ConfiguredModules(modules));

The ConfiguredModules class is a module that configures and then registers the other modules. The implementation is pretty simple:

public class ConfiguredModules : Module
    private readonly IList<Module> modules;

    public ConfiguredModules(IList<Module> modules)
        this.modules = modules;

    protected override void Load(ContainerBuilder builder)
        var settings = ConfigurationManager.AppSettings;
        var keys = settings.AllKeys;

        foreach (var setting in keys)
            var parts = setting.Split('.');
            var moduleName = parts[0];
            var propertyName = parts[1];
            var value = settings[setting];

            var module = modules.First(x => x.GetType().Name == moduleName + "Module");
            var property = module.GetType().GetProperty(propertyName);
            property.SetValue(module, Convert.ChangeType(value, property.PropertyType), null);

        foreach (var module in modules)
A picture of me

Welcome, my name is Paul Stovell. I live in Brisbane and work on Octopus Deploy, an automated deployment tool for .NET applications.

Prior to founding Octopus Deploy, I worked for an investment bank in London building WPF applications, and before that I worked for Readify, an Australian .NET consulting firm. I also worked on a number of open source projects and was an active user group presenter. I was a Microsoft MVP for WPF from 2006 to 2013.

07 Feb 2011

Wow - that's a beautiful idea!

Shingi Mutandwa
Shingi Mutandwa
07 Feb 2011

Great article Paul but the link to structuring an application with Autofac modules is broken.

07 Feb 2011

Thanks Shingi, fixed.

07 Feb 2011

Love it! One question though: What do the properties on the generated Properties.Settings class look like when your application settings have embedded periods?

08 Feb 2011

@Matt, I don't use Properties.Settings. Its an evil, evil piece of junk.

08 Feb 2011

Heh. Yeah, after I posted the comment I realised that you're using <appSettings> rather than <applicationSettings>, so the Properties.Settings class is not involved.

Off topic:

I find Properties.Settings to be very handy for storing user-specific settings. Things like the Window location or the last-used filename. What do you use for that sort of thing? A hand-rolled solution?

09 Feb 2011

That's nice Paul, I like it :)

12 Feb 2011

Why not have the email settings in the database and access them through the 'regular' repository pattern?