TOC

The community is working on translating this tutorial into French, but it seems that no one has started the translation process for this article yet. If you can help us, then please click "More info".

Core Concepts:

Options

In the previous article, we discussed the Configuration mechanism found in the .NET Core framework. It allows you to store application settings in a file - by default in the JSON format, but you can switch to other formats if you want to. However, JSON is a nice format for storing settings - it's short, concise and fast for computers to parse, while remaining easy for a human to read and modify.

In the previous article, we also saw how easy we could access settings stored in the appsettings.json file - using calls to GetSection() and GetValue(), we could fetch the settings based on their names (keys). However, it's generally frowned upon to rely on specifying a string each time you want to access a property: If you change the name of the setting in your configuration file, you will manually have to modify all the places where you reference it and since strings are not checked by the compiler, you may forget to change it in one or several places, which will result in your application failing.

The Options pattern

As an alternative, it's generally recommended to use the Options pattern from ASP.NET Core. It will allow you to create classes with properties to hold your options and the framework will automatically instantiate and populate these classes for you. So, consider the example we used in the previous article, where we stored the title of the website in the appsettings.json file:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "Title": "My ASP.NET Core MVC Website",    
}

We could then create a simple class for it, like this:

public class WebsiteOptions
{
    public WebsiteOptions() { }

    public string Title { get; set; }
}

In the ConfigureServices() method of Startup.cs, we need to tell the framework to use our new class. to do that, we need access to the IConfiguration interface, as shown in the previous article - it will be injected into the Startup.cs class in the top. Be sure to include the Microsoft.Extensions.Configuration namespace:

using Microsoft.Extensions.Configuration;

And then modify your Startup class in Startup.cs to look like this:

public class Startup
{
    private readonly IConfiguration _configuration;
    public Startup(IConfiguration configuration)
    {
_configuration = configuration;
    }

   
    public void ConfigureServices(IServiceCollection services)
    {
services.Configure<WebsiteOptions>(_configuration);
services.AddMvc();
    }
    ....

The interesting line here is the call to services.Configure(). By specifying our class as the type (WebsiteOptions) and referencing the _configuration member (which was injected in the constructor of Startup by the framework) we can connect the configuration file (appsettings.json) with our WebsiteOptions class.

Now it's time to start using these options. It's done much like we did in the previous article, where we ask for our configuration options to be injected into the place where we need to use it, e.g. in the Controller.

Using the Options class in a Controller

To access the options from a Controller, make sure that it has a constructor like this one:

....
using Microsoft.Extensions.Options;
using OptionsSample.Models;

public class HomeController : Controller    
{    
    private WebsiteOptions _websiteOptions;    
    public HomeController(IOptions<WebsiteOptions> websiteOptions)    
    {    
this._websiteOptions = websiteOptions.Value;    
    }  
    ....

Notice how we use Dependency Injection again, this time requesting the framework to inject an instance of the IOptions interface with our WebsiteOptions class as the type. The actual instance of WebsiteOptions will be contained in the Value property of the IOptions interface, as you can see from the code. We can then assign it to a local variable, allowing us to use it throughout any method found on the Controller, e.g. like this:

public IActionResult Index()
{
    return Content("Welcome to " + _websiteOptions.Title);
}

Very simple!

Using the Options class from the Startup class

Some settings are needed already when your application starts and as we saw in the previous article, it's possible to access them from the Startup class. This is also true when using the Options pattern and the code is similar to the approach we used for the Controller. In the Startup class, we usually operate with two methods: ConfigureServices() and Configure(). Since we initialize the Options in the ConfigureService() method, as seen above, and since this method is called before Configure(), we can inject it into the Configure() method and use the options there:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions<WebsiteOptions> optionsAccessor)
{
    string websiteTitle = optionsAccessor.Value.Title;
    ....

As you can see, it's pretty much like using it from a Controller.

Using the Options class from a View

If you need to access your options directly in a View, they can be injected in much the same way as we saw in the previous article. Here's an example:

@using Microsoft.Extensions.Options
@inject IOptions<OptionsSample.Models.WebsiteOptions> OptionsAccessor

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

Welcome to @(OptionsAccessor.Value.Title)

Notice in the second line how we inject the IOptions interface in pretty much the same way we did it in previous examples. We also give the instance a name (in this case OptionsAccessor), which we can then reference when we want to access the options.

Sections/sub-options

To keep things simple, our first examples demonstrated how you could keep an option in the root-scope of the configuration file. However, in a real-world situation, you would likely create one or several sections/suboptions in your configuration file. In fact, the recommended approach is to have one class and one section for each logical group of options you need in your application.

So, in our small demo application, we could change the appsettings.json to group our website related options together, like this:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "Website": {
    "Title": "My ASP.NET Core MVC Website",
    "Version": 1.01
  }  
}

Our WebsiteOptions class should of course reflect the new property (Version):

public class WebsiteOptions
{
    public WebsiteOptions() { }

    public string Title { get; set; }

    public double Version { get; set; }
}

We also need to instruct the framework about where to look for these options, so our call to services.Configure() in Startup.cs needs to be modified. We'll use the GetSection() method to request a specific section to be used when filling the WebsiteOptions instance:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<WebsiteOptions>(_configuration.GetSection("Website"));
    ....

The string we pass as a parameter to the GetSection() method should of course match the key in the configuration file - in this case "Website".

With this in place, our Website options now has its own section in the configuration file. You can add more sections like this, with corresponding Options classes.

The way to access the options remain the same, since we were already accessing an instance of the WebsiteOptions class.

Summary

With a combination of the Configuration mechanism and the Options pattern found in the .NET framework, we can easily store and access application settings in a very robust way in our application.