.NET Core Configuration in .NET 8

Dhananjeyan Balaretnaraja
4 min readMay 25, 2024

--

In this post, I will explore the evolution of configuration in .NET, how the framework handles configuration, and provide practical examples to help you implement these concepts in your applications.

What is Configuration?

Configuration in .NET refers to settings that control the behavior of applications. These settings can include values like retry times, queue lengths, feature flags, and secrets such as connection strings. Traditionally, configuration was applied at compile time, but modern .NET allows for dynamic runtime configuration, enhancing flexibility and power.

Evolution of .NET Configuration

Historically, .NET Framework (versions 4, 4.7, 4.8) used XML-based WebConfig files, accessed via a configuration manager. While this method allowed key-value pair configurations, it lacked support for dependency injection and had a cumbersome transformation syntax.

Here’s an example of a WebConfig file used in .NET Framework:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="RetryCount" value="5" />
<add key="QueueLength" value="100" />
</appSettings>
<connectionStrings>
<add name="MyDatabase" connectionString="Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>

With the advent of .NET Core 5, 6, 7, and now 8, the configuration system was revamped to support various sources like JSON, XML, environment variables, command-line arguments, and even custom providers. These sources are processed in a defined order, providing a unified and dynamic configuration system.

Configuration in appsettings.json

In .NET Core and later versions, JSON is commonly used for configuration. Here is an example of an appsettings.json file:

{
"AppSettings": {
"RetryCount": 5,
"QueueLength": 100,
"Greeting": "Hello, World!",
"Environment": "Development"
},
"ConnectionStrings": {
"MyDatabase": "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

Configuration in Environment Variables

Set the following environment variables

  • Name: AppSettings__Environment
  • Value: Production

Configuration in Command Line Arguments

To add command line arguments in a .NET application, you can pass them when running your application. Here’s an example:

dotnet run -- AppSettings:Environment=Staging Logging:LogLevel:Default=Debug

In the above example, AppSettings:Environment and Logging:LogLevel:Default are the command line arguments being passed.

Overriding Configuration Values

The order in which configuration sources are added determines which values override others. The last provider added will have the highest precedence.

For example, let’s assume that you are adding the configurations the following order

  • JSON file (appsettings.json)
  • Environment variables
  • Command line arguments

Here’s how you can set up the configuration in a .NET Core application with appsettings.json, Environmental Variables and Command Line arguments.

var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddCommandLine(args);

IConfiguration configuration = builder.Build();

In this setup:

  • Values in appsettings.json are loaded first.
  • Environment variables can override values from appsettings.json.
  • Command line arguments can override both appsettings.json and environment variables.

Example Scenarios

Say in a .NET solution you have the following configurations. You have an appsettings.json file with the following configuration and then you set the environment variables and then run the application with the command shown in step 2

Step 1 — appsetting.json

{
"AppSettings": {
"Environment": "Development",
"RetryCount": 5
}
}

Step 2 — Environmental Variable

  • Name: AppSettings__Environment
  • Value: Production

Step 3 — Run the application

dotnet run --AppSettings:Environment=Staging --AppSettings:RetryCount=10

When the application runs, the final values will be:

  • AppSettings:Environment will be Staging (overrides Production from environment variables and Development from appsettings.json).
  • AppSettings:RetryCount will be 10 (overrides 5 from appsettings.json).

Using User Secrets for Development

User secrets are a valuable tool for managing sensitive information during development. They enable secure storage and access to secrets without checking them into source control.

User secrets can be set on your local machine with the following command. Refer this link for setting up user secrets in a Windows environment.

Imagine a scenario where User Secrets, the appsettings.json file, Environmental Variables, and Command Line arguments are all used. The sequence of evaluation or reading the configuration would typically be as follows:

  1. appsettings.json
  2. User Secrets
  3. Environmental Variables
  4. Command Line Arguments
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

// Configure the order of configuration sources
builder.Configuration
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddCommandLine(args);

if (builder.Environment.IsDevelopment())
{
builder.Configuration.AddUserSecrets<Program>();
}

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();
app.UseAuthorization();

app.MapControllers();

app.Run();

Set the user secret using the following command after you’ve setup the user secrets mechanism in your local development machine.

dotnet user-secrets set "AppSettings:Environment" "Development"

Accessing secrets from a cloud provider’s Vault is a topic extensive enough to merit its own blog post, which I will save for a future discussion.

IOptions Pattern

The IOptions pattern in .NET provides a robust way to manage configuration settings with support for reloading and named options. There are three main choices: IOptions, IOptionsSnapshot, and IOptionsMonitor. Each serves different purposes and scenarios, particularly in applications requiring dynamic updates or multiple configurations.

public class AppSettings
{
public string Greeting { get; set; }
public string Environment { get; set; }
}

public class MyService
{
private readonly AppSettings _settings;

public MyService(IOptions<AppSettings> options)
{
_settings = options.Value;
}

public void ShowSettings()
{
Console.WriteLine($"Greeting: {_settings.Greeting}, Environment: {_settings.Environment}");
}
}

.NET 8 introduces compile-time validation for configurations, enhancing efficiency. It supports data annotations and custom validation logic, ensuring configuration integrity before application startup.

public class AppSettings
{
[Required]
public string Greeting { get; set; }

[Required]
[Url]
public string Environment { get; set; }
}

var appSettings = new AppSettings();
configuration.GetSection("AppSettings").Bind(appSettings);
Validator.ValidateObject(appSettings, new ValidationContext(appSettings), validateAllProperties: true);

There are much better or rather cleaner ways to validate configuration values at startup which too I will reserve it for another blog post sometime later.

References

  1. Microsoft Documentation: Configuration in ASP.NET Core
  2. Managing User Secrets in ASP.NET Core
  3. Configuration Providers in .NET

--

--