ASP.NET Core Configuration – Reloading, Binding, Injecting

Introduction

In the last post, we talked about ASP.NET Core Configuration in general.

We saw how is it set up by default from ASP.NET Core.

We also talked about sources and that order matters.

This time we will talk about mapping configuration to classes.

We will also talk about various ways of injecting configuration settings to our application code.

Another useful thing is automatic reload of configuration. We will see how we can have our configuration reloaded while our application is running.

You can find the code with examples here.

 

Binding to models – strongly typed configuration options

Let’s say we have following object in our appsettings.json file:

  "Messages": {
    "ShouldShowAlert": true,
    "AlertMessage": "This is conditional message.",
    "RegularMessage": "This is regular message."
  }

And that we want to get those values and map them to an object.

We would first create a class that matches that structure (or matches only those keys we want to bind):

public class MessagesOptions
{
    public string AlertMessage { get; set; }
    public string RegularMessage { get; set; }
    public bool ShouldShowAlert { get; set; }
}

Now I can bind that object from JSON (section) to an instance of MessageOptions class.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    var messages = new MessagesOptions();
    Configuration.GetSection("Messages").Bind(messages);
}

We can also bind it by using Bind method a bit differently:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    var messages = new MessagesOptions();
    Configuration.Bind("Messages", messages);
}

This is really cool and we could even make a singleton service so we are able to use these values in our code.

However, there is support for this baked in Configuration framework. Read on.

 

Injecting configuration to our code

Another way to get values from our Configuration:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.Configure<MessagesOptions>(Configuration.GetSection("Messages"));
}

Now this means that we can use IOptions<MessagesOptions> anywhere in our application.

IOptions interface has only one member:

public interface IOptions<out TOptions> where TOptions : class, new()
{
    //
    // Summary:
    //     The default configured TOptions instance, equivalent to Get(string.Empty).
    TOptions Value { get; }
}

That means that Value will be an object of a class that we provided, in our case that is MessagesOptions.

I will make a simple Razor Page demonstrating usage of IOptions:

using IOptionsConfiguration.Configuration;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;

namespace IOptionsConfiguration.Pages
{
    public class PartialSettings : PageModel
    {
        public PartialSettings(IOptions<MessagesOptions> iOptionsMessages)
        {
            Messages = iOptionsMessages.Value;
        }

        public string MainMessageFromConfig { get; set; }
        public MessagesOptions Messages { get; set; }

        public void OnGet()
        {
        }
    }
}

Binding the appsettings.json

Another thing we can do is to make a class that will match appsettings.json root object, not only parts of it. Meaning, all values from appsettings.json that have matching property within the class will map to those properties.

Let’s say we have the following appsettings.json:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "CoolestFramework": "Sails.js",
  "MainMessage": "Welcome to the site!",
  "Messages": {
    "ShouldShowAlert": true,
    "AlertMessage": "This is conditional message.",
    "RegularMessage": "This is regular message."
  }
}

And we wanna map “MainMessage” root value and “Messages” object and we are not interested in other keys.

We would have a class like this:

public class CbnSettings
{
    public MessagesOptions Messages { get; set; }
    public string MainMessage { get; set; }
}

public class MessagesOptions
{
    public string AlertMessage { get; set; }
    public string RegularMessage { get; set; }
    public bool ShouldShowAlert { get; set; }
}

Now we can simply do map Configuration to CbnSettings:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.Configure<CbnSettings>(Configuration);
}

This will enable usage of IOptions<CbnSettings> inside of our code.

 

Reloading configuration

For reloading configuration functionality to work, there are two things you need to have:

  1. Reload changes enabled on your source (this is enabled by default for appsettings.json by ASP.NET Core host)
  2. Using IOptionsSnapshot instead of IOptions

If you don’t fancy using IOptionsSnapshot<CbnSettings> in your code (you find it ugly or whatever is the reason), you can do this:

services.Configure<CbnSettings>(Configuration);
services.AddScoped(sp => sp.GetService<IOptionsSnapshot<CbnSettings>>().Value);

After this, you can use CbnSettings settings in your constructor.

public Index(CbnSettings settings)
{
    MainMessageFromConfig = settings.MainMessage;
    Messages = settings.Messages;
}

Whatever you decide on your project, I would just recommend to be consistent.

Ibrahim Šuta

Software Consultant interested and specialising in ASP.NET Core, C#, JavaScript, Angular, React.js.