So I’ve been using this pattern for a while and promising to blog it for almost as long.
Code is on GitHub; package is on NuGet. Here you go :)
How application settings should look:
Here’s a class that needs some configuration settings:
public class EmailSender : IEmailSender
{
private readonly SmtpHostConfigurationSetting _smtpHost;
private readonly SmtpPortConfigurationSetting _smtpPort;
public EmailSender(SmtpHostConfigurationSetting smtpHost,
SmtpPortConfigurationSetting smtpPort)
{
_smtpHost = smtpHost;
_smtpPort = smtpPort;
}
public void Send(MailMessage message)
{
// NOTE the way we can use our strongly-typed settings directly as
// a string and int respectively
using (var client = new SmtpClient(_smtpHost, _smtpPort))
{
client.Send(message);
}
}
}
Here’s how we declare the settings:
// This will give us a strongly-typed string setting.
public class SmtpHostConfigurationSetting : ConfigurationSetting<string>
{
}
// This will give us a strongly-typed int setting.
public class SmtpPortConfigurationSetting : ConfigurationSetting<int>
{
protected override IEnumerable<string> ValidationErrors(int value)
{
if (value <= 0) yield return "TCP port numbers cannot be negative.";
if (value > 65535) yield return "TCP port numbers cannot be greater than 65535.";
}
}
Here’s how we set them in our [web | app].config: |
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="SmtpHostConfigurationSetting" value="localhost" />
<add key="SmtpPortConfigurationSetting" value="25" />
</appSettings>
</configuration>
… and here’s how we provide mock values for them in our unit tests:
var smtpHost = new SmtpHostConfigurationSetting {Value = "smtp.example.com"};
var smtpPort = new SmtpPortConfigurationSetting {Value = 25};
var emailSender = new EmailSender(smtpHost, smtpPort);
emailSender.Send(someTestMessage);
Getting started
In the NuGet Package Manager Console, type:
Install-Package ConfigInjector
then run up the configurator like this:
ConfigurationConfigurator
.RegisterConfigurationSettings()
.FromAssemblies(/* TODO: Provide a list of assemblies to scan for configuration settings here */)
.RegisterWithContainer(configSetting => /* TODO: Register this instance with your container here */ )
.DoYourThing();
You can pick your favourite container from the list below or roll your own.
Getting started with Autofac
var builder = new ContainerBuilder();
builder.RegisterType<DeepThought>();
ConfigurationConfigurator
.RegisterConfigurationSettings()
.FromAssemblies(typeof (DeepThought).Assembly)
.RegisterWithContainer(configSetting => builder.RegisterInstance(configSetting)
.AsSelf()
.SingleInstance())
.DoYourThing();
return builder.Build();
Getting started with Castle Windsor
var container = new WindsorContainer();
container.Register(Component.For<DeepThought>());
ConfigurationConfigurator
.RegisterConfigurationSettings()
.FromAssemblies(typeof (DeepThought).Assembly)
.RegisterWithContainer(configSetting => container.Register(Component.For(configSetting.GetType())
.Instance(configSetting)
.LifestyleSingleton()))
.DoYourThing();
return container;
Getting started with Ninject
var kernel = new StandardKernel();
kernel.Bind<DeepThought>().ToSelf();
ConfigurationConfigurator
.RegisterConfigurationSettings()
.FromAssemblies(typeof (DeepThought).Assembly)
.RegisterWithContainer(configSetting => kernel.Bind(configSetting.GetType())
.ToConstant(configSetting))
.DoYourThing();
return kernel;