Strongly typed configuration in ASP.NET Core without IOptions<T>

There are several great resources on the Internet about using the new Configuration and Options framework of ASP.NET Core – like this comprehensive post by Rick Strahl.

Using strongly typed configuration is without a question a great convenience and productivity boost for the developers; but what I wanted to show you today is how to bind IConfiguration directly to your POCO object – so that you can inject it directly into the dependent classes without wrapping into IOptions.

POCO configuration with IOptions<T>

The typical IOptions<T> driven configuration setup would look like on the snippet below. To use this code, you also need to reference the Microsoft.Extensions.Options.ConfigurationExtensions package, which will expose the extension methods and will also bring in the options framework package as a dependency.

This will allow you to load up MySettings from appsettings.json into MySettings POCO.

However, using the options framework also means that your configuration is registered in the DI container as IOptions<MySettings>, and that’s how you will need to inject it.

This typically wouldn’t matter but it also means that you will need to reference the Microsoft.Extensions.Options package everywhere where you want to consume this configuration (that’s where IOptions<T> is defined).

In other words, your POCO configuration is not exactly POCO anymore, as it drags the extra dependency alongside it, as you always consume it like this:

It also doesn’t give you access to the POCO configuration instance straight away – it just buries it directly into the DI, so to access it immediately in the Startup class, you’d need to resolve it from DI.

EDIT: On top of that – as mentioned by Steven – the problem with IOptions<T> is that it will defer the evaluation of the configuration. This means that if your configuration file is incorrect, your app can crash later on, the first time IOptions<T>.Value is accessed, rather than at startup.

POCO configuration without IOptions<T>

You could, however, register the POCO configuration manually, and avoid the extra dependency on the options framework. This is shown below.

We are manually binding the configuration to the POCO, rather than having the options framework do it for us. This functionality is available in the Microsoft.Extensions.Configuration.Binder package, which needs to be referenced (it is not an extra dependency for us though, it is already referenced by the Microsoft.Extensions.Options.ConfigurationExtensions package). It also needs to be referenced only in the entry project (or your composition root).

We can now consume this extension method as follows:

And you can now use your POCO configuration from the DI simply by injecting it, without any external dependencies.

Another small limitation of the options framework is that the POCO configuration class must have a parameterless constructor, as the framework will try to instantiate it for us.

However, with our custom approach, we can control how our class is instantiated. We could write extra extension methods that either take in an already existing instance, or a delegate responsible for its creation.

We can now consume these extension methods as its shown on the next snippet, without having to worry about exposing a default constructor. The instance could be created upfront or on demand through the delegate.

This type of approach allows you to combine your POCO configuration classes with runtime data stretching beyond the configuration file – for example information you’d read from IHostingEnvironment.

  • .NET Junkie

    I did an analysis of the options library last year and came to the conclusion that it is close to useless. Another argument of why injecting IOptions is bad is because it causes the
    deserialization to be done lazily, which causes an application to fail at runtime, instead of during start-up in case of a configuration error. You can read my whole reasoning here:

    • Filip W

      ah great point – I totally forgot about the lazy evaluation! agree 100%

  • Sock

    Nice, Khalid achieves a somewhat similar result to avoid a dependency on IOptions here:

    Really liking the ConfigurePOCO extension methods… I guess the use of IOptions vs POCO may become more useful if it gets some reloading capabilities back (e.g. changing the underlying JSON file causes the IOptions value to be updated)

    • Muhammad Rehan Saeed

      Yes, reloading is the only reason to use IOptions. Is reloading enough of a killer feature to warrant IOptions, I’m not sure? IOptions also means you have to use Moq or stub out IOptions in your tests which is annoying to do.

  • Paul Wheeler

    Simple yet brilliant!

  • markrendle

    Love it. Thanks for sharing this.

  • Dominick Baier

    That’s how you get around the IOptions dependency in your code:

    resolver => resolver.GetRequiredService<IOptions>().Value);

  • Dominick Baier

    But – yeah – not a huge fan of IOptions either – the nice little feature is that it allows registering it a couple of times. This allows for scenarios like partial config and overriding config at a later point.

    We use that to modify the ASP.NET Identity config in our integration layer.

  • Manoj Kulkarni

    Thank you for sharing