Skip to content

A Man – With a plan

My thoughts and curation on software development and tech

Menu
  • Home
  • About
Menu

Using Configuration and Options with .NET Core 3.1

Posted on January 20, 2020January 20, 2020 by Andy
Reading Time: 6 minutes

In this blog post, we explore how we can configure settings. We start by answering the questions. What are configuration settings? Why do we need them? And how do we use configuration settings properly in .NET projects?

What are configuration settings and why do we use them?

With configuration, we define settings for our applications to be accessed in other parts of the code. Application configuration provides settings that are loaded during startup. These settings can differ from environment to environment, like your development, test and production environment. They also can change over time, for example, you have API keys that change once in a while. We want to separate this from our code, so we do not have source code changes, which requires recompilation and deployment of the application.

How do we use configuration settings?

When we create .NET Core 3.1 applications the NuGet package Microsoft.Extensions.Configuration is referenced by default. This provides functionality to interact with configuration settings. There is also the Options pattern which uses classes to represent groups of related settings. With three examples we will explore how we retrieve configuration settings.

Example 1

Settings are defined as key-value pairs. The key is used to identify the setting and the value contains the data. Each configuration setting will have a unique key to access the configuration value at runtime. The settings are hierarchically organized. Related configuration can be grouped in sections, which we will see later. The hierarchy is flattened in order to access the configuration values. In this example we will see how the colon (:) separator to combine the sections in the hierarchy to locate the value.

In the application.settings.json file, place the following section:

So the file looks like:

In this first example, we make use of dependency injection to inject the configuration in the HomeController. During Startup the default implementation of IConfiguration will be retrieved and assigned to _configuration.

Add IConfiguration configuration  as a parameter in the constructor and add the following field above the constructor.

private readonly IConfiguration _configuration;

Initialize the read-only field _configuration

_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));

The result should look like this:

Now we can read the BaseUrl from the application.json file within the Index method.

First, we create the class Configurations.cs in the Models folder.

The BaseUrl is within the section SiteConfiguration which is within the section Example1. To traverse the sections we make use of the colon :. We have two sections so we use two :, as in

We assign configurations as the model in the line

The Index method should look like

Now we can show the BaseUrl in the view. Open Views\Index.html.

We add the model on the first line

@model Configurations

We add

<p>BaseUrl: @Model.BaseUrl</p>

before the closing </div>

The Index.html file will look like

Now you can start the project with ‘F5’ and see the result

Example 2

In this example, we will make use of Options pattern. We change the Configurations class in the Models folder to

This mirrors the hierarchy of the sections in appsettings.json.

In Startup.cs in the ConfigureServices method we add

services.Configure<Models.Example2>(Configuration.GetSection(nameof(Example2)));

With this line of code, we retrieve the section with the name Example from appsettings.json. Since the hierarchy and the naming is the same this is mapped correctly. We will see this later when we use it in the view.

In the HomeController class, we add the parameter

IOptions<Models.Example2> settings,

The dependency container will give us an instance of the Example2 class, since we configured this in the Startup class.

We add the following field above the constructor

private readonly Models.Example2 _example2;

and initialize it in the constructor. The result should look like

In the Index method we create a Configurations variable and assign _example2 to its Exampl2 property.

Now we can show the BaseUrl in the view. Open Views\Index.html.

We add at the first line

@model Configurations

We add

<p>BaseUrl: @Model.BaseUrl</p>

before the closing </div>. The Index.html file will look like

Now you can start the project with ‘F5’ and see the result

Example 3

In this example, we will validate when settings are missing. We start with the following settings in application.json where BaseUrl is commented out.

We change the Configurations.cs to

In the class SiteConfiguration we annotated the properties BaseUrl and Key with the Required attribute. This will trigger an exception if the properties are not set. In order to make it clearer which property threw the error we add ErrorMessage properties to the attributes.

In Example2 we used services.Configure<Models.Example2>(Configuration.GetSection(nameof(Example2))); in Startup.cs. In order to trigger the validation, we change this line in the method ConfigureServices to

Pitfall 1: Since SiteConfiguration is nested within Example3 in appsettings.json, the following will not work.

We have to call explicitly "Example3:SiteConfiguration". Another option is to remove the Example3 outer section in appsettings.json.

Then SiteConfiguration is at the top-level.

Pitfall 2: Since the Required attributes are on the properties within the class SiteConfiguration

will not trigger the validation.

In the Home controller we have

In Index.html we have

When we start the project, we will see

This is triggered when the Home controller is called to serve the Index page. If we had another controller and we opened a page on that controller first, then the Home controller is not activated and will not throw a validation exception.

In order to trigger validation when the project is starting, we have to add the following changes.

  1. Add the Microsoft.Extensions.Hosting NuGet package to the Example3 project.
  2. Implement the IHostedService interface to trigger the validation.
  3. Add the Hosted Service to the ConfigureServices method in Startup.cs.

Step 1

  • Open the Package Manager Console,
  • Select Example3 as the Default project and
  • Execute the line Install-Package Microsoft.Extensions.Hosting.

Step 2

We have to add another Controller in order to see that the error will not be triggered. I assume you know how to create a new Controller. We name the controller WeatherController. The controller has only 1 method which returns the index.

The View associated with the Index method is in the folder Views\Weather and is named Index.cshtml.

We change the launchSettings to navigate to this page. We open Properties and edit launchSettings.json.

We add the line "launchUrl": "https://localhost:44369/weather/" to the IIS Express section.

When we start the project. We see that the application works fine. If we press the Home-link, the familiar Error-page is shown. We want to check if the required settings are set during startup, which brings us to step 3.

Step 3

Create the folder Validations. Create the class ValidateOptionsService in it. Implement the interface IHostedService. Generate the methods we have to implement.

public class ValidateOptionsService : IHostedService

Paste the following code in the class

The parameter IHostApplicationLifetime hostApplicationLifetime is needed to stop the application when there are exceptions. This will be shown later.

The parameter IOptions<SiteConfiguration> settings is the one we want to validate.

We added a logger to output the exceptions to the Output window or we can log it in Application Insights, which is outside the scope of this tutorial.

We implement the two methods StartAsync and StopAsync which the interface IHostedService require

In Startup.cs add the following lines to the method ConfigureServices

// Do not forget tot add new Settings to the ValidateOptionsService
services.AddHostedService<ValidateOptionsService>();

which results in

When we start the project, ValidateOptionsService will be triggered and the required settings of SiteConfiguration will be checked and in the Output window we will see the following error:

Pitfall 3: If you add new settings, for example for your Storage like Azure CosmosDB. You create a StorageSettings class and decorate the properties with the Required attribute. In order to trigger the validation in ValidateOptionsService, you have to add a parameter to the constructor

IOptions<StorageConfiguration> storageSettings

And trigger the validation in the StartAsync method in the try block with

_ = _storageSettings.Value;

Summary

In this tutorial, we have seen how we can read one setting in Example 1:

_configuration["Example1:SiteConfiguration:BaseUrl"]

In Example 2 we have seen how we can map settings to classes. In Example 3 we have seen how we can validate required settings during the startup of the application.

In another tutorial, I will discuss UserSecrets and the Azure KeyVault because storing our secrets in source code is a severe vulnerability.

Code

You can find the code at Github.

Endnotes

I first came in contact with IOptions at a freelance project, later I learned about the PluralSight course Using Configuration and Options in .NET Core and ASP.NET Core Apps from Steve Gordon. Example 3 is based on this course. I am thinking about how to avoid adding a new IOptions<NewSettings> each time in the constructor of ValidateOptionsService. If you have a solution to how to solve this, I would like to hear.

You can read more at:

  • Configurations in ASP.NET Core – Microsoft docs
  • Options pattern in ASP.NET Core – Microsoft docs
  • Order of Precedence when Configuring ASP.NET Core
Subscribe
Notify of
guest
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments

Recent Posts

  • DotNed Saturday 2020 Event – Thoughts & Highlights
  • Using Configuration and Options with .NET Core 3.1
  • A new year, a new decade & a new attempt

Archives

  • January 2020

Categories

  • Uncategorized

Tags

.net core .net core 3.1. application inspector azure azure application configuration c# configuration management event freelance ioptions living documents start blogging

Recent Comments

    ©2022 A Man – With a plan | Built using WordPress and Responsive Blogily theme by Superb
    wpDiscuz