(This article is based on .NET Core SDK 1.0.0-preview2.1-003155 and "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0-*")

There are some improvements of the way we get access to our configuration files in ASP.NET Core. The time as we did assign the properties of our config classes by ourselves and we have to deal with config entry keys is gone. With Microsoft.Extensions.Configuration and the Microsoft.Extensions.Options package we now have the ability to map our configuration sections to POCO's and inject them where we need it.

This is very helpful for a cleaner design not least since because the ASP.NET Core framework gives us the ability to store our configuration on different places. These places for example could be files (e.g. appsettings.json, $"appsettings.{env.EnvironmentName}.json"), environment variables, command line arguments an something else. So let us explore what paths we are now open to, and if you would try this by yourselves, you could find the sample code on GitHub in a SampleProject.

ConfigurationBuilder

The entry point of our configuration system is the class ConfigurationBuilder. This is the place where we can configure our configuration sources. After calling the Build method, we have full access to the configuration by using the IConfigurationRoot interface which also inherits the IConfiguration interface.

public IConfigurationRoot Configuration { get; }

public Startup(IHostingEnvironment env)  
{
    var builder = new ConfigurationBuilder()
        .AddCommandLine(Environment.GetCommandLineArgs())
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

To read now our configuration values we can use some of the interface methods like them shown below.

//Gets or sets a configuration value.
string this[string key] { get; set; }

//Gets the immediate descendant configuration sub-sections.
IEnumerable<IConfigurationSection> GetChildren();

//Gets a configuration sub-section with the specified key.
IConfigurationSection GetSection(string key);  

IOptions and IOptionsMonitor

And now let's take a look onto the new and recommended way to get access to the configuration values. This method named Options pattern is, as I think, a much better way doing this.

Firstly we have to create a POCO. The properties of this are representing our configuration settings/entries. For this class it is not necessary to inherit from any other class (for example there is no ConfigurationBase class or so).

public class MySettings  
{
    public string MySettingsEnry1{get; set;}
    public List<string> MySettingsEnryList{get; set;}
    public int MySettingsNumber{get; set;}
}

Attention: IEnumerable instead of List would not work!

As you can see, you create it with with strongly typed properties, not only 'everything is a string' and map it to a corresponding JSON section of our settings file. As a sample of this, we can map the JSON configuration section below with the class above according you have configured the binding as described in the next section.

{
  "MySettings" :{
    "MySettingsEnry1":"Value",
    "MySettingsEnryList":[
       "A",
       "B"
    ],
    "MySettingsNumber": 10
  }
}

Startup (configure the options bindings)

To use the configuration POCO's with the Options Pattern we now have to do some minimal configuration effort. But this is not so much work, it can easily be done in the ConfigureServices method of the Startup class.

In this method we have to define the mapping of the POCO's and the Section's of the configuration file. There we have to call services.Configure<POCONAME>(CONFIGSECTION) for every single mapping. And after this we than have to register the needed services in the Ioc of ASP.NET Core by calling services.AddOptions(). These few steps are shown in the next code snipet.

public class Startup  
{
    public IConfigurationRoot Configuration { get; }

    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<MySettings>(Configuration.GetSection("MySettings"));
        services.AddOptions();

        services.AddSingleton<IMySampleService, MySampleService>();
        ...
        services.AddMvc()
    }
}

Use your options

So now after we have configured all our needs I will show how we can use it in action. At this point we have two choices. First we take a look at the normal way where we only get an instance of our configuration POCO to read (and also write) our settings.

(Notice: Writing the settings could only change the value in the memory, not in the file. If you use the IOptions<MySettings> in different services, there will be an instance per service, which means if you change a settings value in service A, the settings value in service B will not be affected.)

public class MySampleService  
{
    private IOptions<MySettings> _settings;

    public MySampleService(IOptions<MySettings> settings)
    {
        _settings = settings; 
        //access options with _settings.Value.[PropertyName]
    }
}

Now the second and more cool way is to use IOptionsMonitor which gave us also access to the configuration. But, in this variant we always have access to the current value also even the values of the configurations will get changed.(This means, another service which also has an injected IOptionsMonitor changes the settings in memory and also when the configuration file was changed). If we want to be informed about the changes of our files, we additionally have to configure reloadOnChange: true in our ConfigurationBuilder.

public class MySampleService  
{
    private IOptionsMonitor<MySettings> _settings;
    private IDisposable _configWatcher;

    public MySettings Settings{ get{ return _settings.CurrentValue; }}

    public MySampleService(IOptionsMonitor<MySettings> settings)
    {
        _settings = settings;
        _configWatcher = _settings.OnChange(ConfigChanged); 
        //access options with _settings.CurrentValue.[PropertyName]
    }

    private void ConfigChanged(MySettings currentConfig)
    {
    }
}

As the sample above shows, we always have access to the current configuration data by using the Settings property, because this points always on the CurrentValue of the settings POCO.
If you also need to get informed of a configuration change in your code, you could register the OnChanged Action of the IOptionsMonitor.

One thing is missing

As cool as this stuff already is, one thing is missing. In some of my projects i would like to change some configuration parts at the run time and then persist it to the file or so. But at the time of writing this post, there was no way implemented to do this. Nevertheless as the GitHub-Page of the ASP.Net team shows, this feature is on the backlog.

Summary

I think this extension of configuration is very helpful to create a clean and easy way to access your configuration data. I am very interested in how this component will evolve and what exciting functions will still be waiting for us.

In the reference section and in the language integration section you can find some additional links I used to learn and understand the functionality. I hope you find this article helpful. If this is the case or if you have some questions or find some bugs, please post in the comment section below. I will thankfully use your input to improve this blog-post

References