Basildon Coder
  • Home
  • Categories
  • Tags
  • Archives

A Handy Settings Pattern

.Net has a fairly nice strongly-typed settings file, but unfortunately the most common pattern of access is the big fat static accessor Settings.Default. Whilst refactoring some old code recently to use dependency injection, I found myself struggling a bit to manage settings nicely so that this static wart worked correctly. I came up with the following little pattern which, so far, works well for me so I thought I'd share it.

Firstly, in any project that needs runtime configuration, define an interface that contains readonly properties for the data it needs.

namespace MyClassLibraryProject
{
    public interface ISettings
    {
        string MyStringSetting { get; }
        bool SomeFlag { get; }
    }
}

Then in any class that depends on the settings, make sure the constructor accepts that interface (the DI container will inject it).

namespace MyClassLibraryProject
{
    public class MyConfigurableClass : IMyConfigurableClass
    {
        private readonly ISettings _settings;

        public MyConfigurableClass(ISettings settings)
        {
            _settings = settings;
        }
    }
}

Then, in whatever project builds your actual executable (e.g. a Console app, Windows Service, or even just your unit tests) define your Settings.settings file as normal, making sure that it contains settings names that match the properties you defined. This will generate a Settings class for you with the properties needed to implement the interface defined by your class library.

namespace MyExecutableProject.Properties {


    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(
        "Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator",
        "12.0.0.0")]
    internal sealed partial class Settings :
        global::System.Configuration.ApplicationSettingsBase {

        private static Settings defaultInstance = ((Settings)
            (global::System.Configuration.ApplicationSettingsBase.Synchronized(
                new Settings())));

        public static Settings Default {
            get {
                return defaultInstance;
            }
        }

        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute(
            "some string value")]
        public string MyStringSetting {
            get {
                return ((string)(this["MyStringSetting"]));
            }
        }

        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("False")]
        public bool SomeFlag {
            get {
                return ((bool)(this["SomeFlag"]));
            }
        }
    }
}

Importantly, this generated class is partial, so you can extend it. The extension doesn't need to do anything other that declare that it implements the settings interface.

// ReSharper disable once CheckNamespace
namespace MyExecutableProject.Properties
{
    internal partial class Settings : MyClassLibraryProject.ISettings
    {
    }
}

Finally, in your composition root map the static accessor for this Settings class to the interface. In this example I'm using SimpleInjector.

var container = new Container();
container.RegisterSingle<MyClassLibraryProject.ISettings>(Settings.Default);
container.Register<MyClassLibraryProject.IMyConfigurableClass,
    MyClassLibraryProject.MyConfigurableClass>();

SimpleInjector will now automatically inject Settings.Default into every instance of MyConfigurableClass, behind the ISettings interface. This of course makes it trivial to mock settings in tests to check configurable behaviour without having to muck around with a settings file.

[Test]
public void TestSomething()
{
    var settings = Mock.Of<ISettings>(
        s => s.MyStringSetting == "test with this value");
    var sut = new MyConfigurableClass(settings);

    Assert.IsTrue(sut.DoesTheRightThing());
}

As a bonus, because this is all strongly-typed, automated renames work as you expect. Rename a property on the interface and it will rename the property on the Settings class automatically (at least, Resharper does - not sure about vanilla Visual Studio).


  • « Windows Debugging Armoury
  • PostgreSQL, JDBC, and Client Certificates »

Published

Feb 20, 2015

Category

coding

Tags

  • coding 13
  • .net 7
  • patterns 3

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor