The .NET Configuration System

20 Jul 2024 20 Jul 2024 3 min read .NET C#

The .NET configuration system provides a standardized way of expressing and reading configuration values.

NuGet Package: Microsoft.Extensions.Configuration
Source Code: https://github.com/dotnet/runtime/tree/main/src/libraries

Hierarchical Configuration Values 

Hierarchical configuration values are supported out-of-the-box. For example here the logging configuration:


{
    "Logging": {
        "Console": {
            "FormatterOptions": {
                "TimestampFormat": "[HH:mm:ss] "
            }
        }
    }
}

If a configuration provider doesn’t support a hierarchical representation but just flat strings (e.g. the command line, environment variables), hierarchy levels are separated with a :

Logging:Console:FormatterOptions:TimestampFormat

Environment variables don’t support : as hierarchy separator - they use __ (double underscore) instead:

Logging__Console__FormatterOptions__TimestampFormat

Getting a Value 

To get a configuration value, you need an instance of IConfiguration (for how to get this, see below).

With this, you can call:


IConfiguration config = ...
int value = config.GetValue<int>("Parent:FavoriteNumber");

Note: GetValue<T>() is an extension method from the Microsoft.Extensions.Configuration.Binder NuGet package.

Alternatively you can bind whole configuration sections (see below) to objects.

For example, with this configuration:


{
    "Settings": {
        "KeyOne": 1,
        "KeyTwo": true,
        "KeyThree": {
            "Message": "Oh, that's nice..."
        }
    }
}

And these classes:


public class Settings
{
    public int KeyOne { get; set; }
    public bool KeyTwo { get; set; }
    public NestedSettings KeyThree { get; set; }
}

public class NestedSettings
{
    public string Message { get; set; }
}

You can call:


IConfiguration config = ...
Settings settings = config.GetRequiredSection("Settings").Get<Settings>();

Central Interfaces 

There are three central interface for the configuration system:

Both IConfigurationRoot and IConfigurationSection inherit from IConfiguration.

Conceptually both IConfigurationRoot and IConfigurationSection represent a configuration hierarchy.

Configuration Sections 

Configuration sections allow you to get a whole subtree (sub hierarchy) of the configuration hierarchy.

To obtain a configuration section use this extension method:


IConfigurationSection GetRequiredSection(this IConfiguration configuration, string key)

For example, given this configuration:


{
    "Logging": {
        "Console": {
            "FormatterOptions": {
                "TimestampFormat": "[HH:mm:ss] "
            }
        }
    }
}

Calling configurationRoot.GetRequiredSection("Logging") gives you (conceptually):


{
    "Console": {
        "FormatterOptions": {
            "TimestampFormat": "[HH:mm:ss] "
        }
    }
}

Accessing the Configurations via Generic Host 

If you’re using a Generic Host application, you have several possibilities to get access to the configuration system:

  • In IHostBuilder via HostBuilderContext.Configuration (HostBuilderContext can be injected as parameter in the delegates registered in IHostBuilder)

  • Via dependency injection (as IConfiguration)

  • Via Options. They can be registered in the dependency injection system like so:

    
    hostBuilder.ConfigureServices((context, services) =>
    {
        services.Configure<MyOptions>(context.Configuration.GetSection("MyOptions"));
    });
    

    And can then be injected in a constructor like so:

    
    public ExampleService(IOptions<MyOptions> options) { }
    

Manual Creation 

If you don’t use a Generic Host application, you can manually create the configuration system like so:


IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddEnvironmentVariables()
    .Build();

Configuration Providers 

Configuration Providers define where configuration values are read from.

They’re registered in the IConfigurationBuilder like so:


configurationBuilder.AddJsonFile("appsettings.json");
configurationBuilder.AddEnvironmentVariables();

With IHostBuilder, providers are registered with these methods:


// host configuration
ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate);

// application configuration
ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate);

Note: The order in which the providers are registers matters! If the same configuration key is provided by multiple providers, the provider that was added last is used.

.NET comes with several configuration providers built-in. Here a few select ones.

In-Memory Provider 


configurationBuilder.AddInMemoryCollection(
    new Dictionary<string, string>
    {
        ["SecretKey"] = "Dictionary MyKey Value",
        ["TransientFaultHandlingOptions:Enabled"] = bool.TrueString,
        ["TransientFaultHandlingOptions:AutoRetryDelay"] = "00:00:07",
        ["Logging:LogLevel:Default"] = "Warning"
    }
);

JSON File Provider 


hostBuilder.ConfigureAppConfiguration((hostBuilderContext, configurationBuilder) =>
    {
        IHostEnvironment env = hostBuilderContext.HostingEnvironment;

        configurationBuilder
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);
    });

Command Line Provider 


configurationBuilder.AddCommandLine(args);

Configuration values can be set with various patterns - for example like this:


--Parent:FavoriteNumber 42

Environment Variables Provider 

Provides configuration values via environment variables.

By default, the variables do not require a prefix. However, you can specify that only variables with a certain prefix are to be used:


configurationBuilder.AddEnvironmentVariables(prefix: "CustomPrefix_");

For example, to set the TimestampFormat for the console logger via environment variable, use this:


set Logging__Console__FormatterOptions__TimestampFormat="[HH:mm:ss] "