Convention Based Application Settings via Autofac

On November 29, 2011, in projects, by Josh Bush

A while back I stumbled on a blog post from Chad Myers about a cool feature of FubuCore around handling application configuration. It was one of those “duh, why didn’t I think of this years ago” moments. You see, I’ve inherited an application which has a bunch of configurable options in the app.config. The code had already isolated those settings to classes with virtual members and a lot of calls to ConfigurationManager.AppSettings. It was testable by inheriting the settings class and overriding all of the properties. After seeing Chad’s post though, I had to scrap it. POCO Settings — yes please.

I’ve started a project to provide this functionality with Autofac. The project is still very new, currently I’ve only implemented sourcing settings via (app|web).config. I’m hoping to push in support for environment variables and the registry as sources for settings. Additionally I’m going to make the conventions easier to override since you might not like your classes ending in “Settings”.

Let’s go through some of the code and see how it all works together.

The coordinator for the whole shebang is the Autofac registration source:

public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor) {
    var s = service as IServiceWithType;
    if (s != null && s.ServiceType.IsClass && s.ServiceType.Name.EndsWith("Settings")) {
        yield return RegistrationBuilder.ForDelegate((c, p) => c.Resolve<ISettingsFactory>().Create(s.ServiceType))
            .As(s.ServiceType)
            .SingleInstance()
            .CreateRegistration();
    }
}

This looks for types to resolve whose name ends with “Settings”. When it’s time to actually create the type, we’ll ask the container for a Settings Factory and tell it to create the type being asked for. Then we’ll register that in the container as a singleton. Not too much going on there.

The Settings Factory is responsible for creating the settings object, polling each settings provider for values, setting the values and verifying that it’s all good before shipping it off:

public class SettingsFactory : ISettingsFactory {
    readonly IEnumerable<ISettingsProvider> _providers;

    public SettingsFactory(IEnumerable<ISettingsProvider> providers) {
        _providers = providers;
    }

    public object Create(Type type) {
        var obj = Activator.CreateInstance(type);

        var propertiesMissed = PopulateProperties(type, obj);

        if (propertiesMissed.Any()) {
            var message = String.Format("{0} is missing the following properties in configuration: {1}", type.Name, String.Join(",", propertiesMissed));
            throw new ConfigurationErrorsException(message);
        }

        return obj;
    }

    List<string> PopulateProperties(Type type, object obj) {
        var propertiesMissed = new List<string>();

        var props = type
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.CanWrite);

        foreach (var prop in props) {
            var value = _providers
                .Select(p => p.ProvideValueFor(type, prop.Name))
                .FirstOrDefault(p => p != null);
            if (value != null)
                prop.Set(obj, value);
            else
                propertiesMissed.Add(prop.Name);
        }
        return propertiesMissed;
    }
}

This class is where most of the work is happening. I’m not totally sold on the implementation, but it will do for now. Right now I’m just taking the first value I get which will prioritize the first registrations.

The last bit is an implementation of a Settings Provider. ConfiguraionManager.AppSettings is just a NameValueCollection, so that makes things stupid easy. The Name/Value Provider looks like this:

public class NameValueSettingsProvider : ISettingsProvider {
    private readonly NameValueCollection _source;

    public NameValueSettingsProvider(NameValueCollection source) {
        _source = source;
    }

    public object ProvideValueFor(Type type, string propertyName) {
        var key = type.Name + "." + propertyName;
        return _source[key];
    }
}

If we have a FooSettings class with a property Bar then we’d expect to see “FooSettings.Bar” as a key. Really easy, nothing to see here.

The Payoff
Now, let’s see it all together. Here’s the spec that shows everything in play:

class FooSettings {
    public string Bar { get; set; }
}

class NeedsSomeSettings {
    public NeedsSomeSettings(FooSettings settings){
        Settings = settings;
    }

    public FooSettings Settings { get; private set; }
}

public class when_resolving_a_class_that_depends_on_settings {    
    static IContainer _container;
    static NeedsSomeSettings _needsomeSettings;

    Establish context = ()=> {
        var builder = new ContainerBuilder();
        builder.RegisterSource(new SettingsSource());
        builder.RegisterType<SettingsFactory>().AsImplementedInterfaces();
        builder.RegisterType<NameValueSettingsProvider>()
            .WithParameter("source", new NameValueCollection() {{"FooSettings.Bar", "w00t!"}})
            .AsImplementedInterfaces();
        builder.RegisterType<NeedsSomeSettings>().AsSelf();
        _container = builder.Build();
    };

    Because of = () => _needsomeSettings = _container.Resolve<NeedsSomeSettings>();

    It should_resolve_type = () => _needsomeSettings.ShouldNotBeNull();
    It should_have_settings = () => _needsomeSettings.Settings.ShouldNotBeNull();
    It should_have_settings_populated = () => _needsomeSettings.Settings.Bar.ShouldEqual("w00t!");
}

No more mucking around with problematic configuration. Just take a dependency on your POCO settings and off you go. If you forget to configure something you get a friendly error message. Like I said, there are bits of the API I’m not toally sold on and I have few more sources to implement. I also need to add a proper Autofac module to hide a lot of the registration noise.

So far I’m liking this a lot better than having random references to ConfigurationManager.AppSettings littered throughout the code. What do you think? Follow along on github and let me know what you like or hate.

Tagged with:  
  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #992

  • Pingback: Convention Based Settings with Unity « hypnocode

  • Dave Robbins

    Nicely written post.  I’ve been struggling with the same issues this past month – I want to eliminate some much boilerplate code with AppSettings as I get uncomfortable with a long list of key value pairs for everything. 

    I have thus avoided using IOC containers for the solution but your code is starting to convince me otherwise.  I currently use Stateless by Nick Blumhardt and like his style of coding.  I look forward to more from you.

    • Anonymous

      Thank you for the feedback. Using this technique doesn’t require an IOC container, but it sure makes it convenient. I like being able to specify what my object will need in the constructor and then never having to think about it again.