Application configuration in .Net

Effective application configuration management is crucial for ensuring adaptability and maintainability. It empowers applications to seamlessly adapt to diverse environments, from development to production, and dynamically adjust settings like external service URLs.

Types of configuration

Configuration can be broken down in many different ways, I like to categories it in the following groups:

Environmental Configuration

This is configuration that will change between environments some good examples are

  • Database Connection strings
  • Tenant name (in a tenanted environment)
  • Authentication Authority

Service Configuration

This is configuration that are used to control some of the behaviors of the application, it will not generally change between environments, some common examples are:

  • Database Connection Pool sizes
  • Timeouts and retry policies
  • Processing chunk sizes
  • Authorisation role definitions

Feature Flags

Feature Flags are used to enable or disable specific features, they also come in quite useful when deploying new features as they all features to be deployed turned off or enabled for only a portion of users and disabled if there is found to be an issue with the feature, this style of development has become popular as it de-risks a deployment as it can often be turned off with the simple action of disabling a flag. I would recommend having any feature flags that are used specifically for feature deployment be short lives and decommissioned as so as it is no longer required.

Domain Specific Configuration

This is data specific to the application domain and most frequently found inside of a database, a lot of engineers do not consider this configuration but I feel at its essence it is, if the application was for a shop, products would be domain specific configuration.

General advice on configuration

I have seen many examples of configuration in my time, and I would recommend the following guidelines:

  • Default everything that can be.
  • Have the smallest amount of Environmental Configuration as possible, given that the applications should be almost identical between environments there is not much that should need to change.
  • Never have two pieces of configuration that need to match (i.e. Database Connection String and Database Name), I have lost more hours that I wish to admit troubleshooting issues caused by this.
  • Give configuration sensible names and document what they do.
  • Do not have Environment Name as a piece of configuration, too many times have I seen code such as if(EnvironmentName=="Test") this often hides the intent and also leads to confusing situations later on when the application operates differently in a test environment. Ideally all environments will follow the same code branches, this will help to ensure the predictability of how applications run in all environments.

Configuration and Testing

Configuration and testing often go hand in hand as configuration can be used point an instance of an application at stubbed services for Level 3 testing, or Feature Flags can be used to enable features in a test environment prior to switching them on in a production environment.

Configuration can also be the enemy of testing, taking a purist view, any piece of configuration that can be changed, should be test in combination with any other. If you consider Feature Flags, if an application has 5 feature flags, then to get full coverage the application should be tested with every possible combination resulting in 32 possible combinations, adding a sixth would take the number to 64 and seven to 128. It is easy to see how adding configuration items can get out of hand and this is due to the fact that things like Feature Flags create branching code.

Configuration in .Net

Microsoft provide a robust solution to configuration in .Net and AspNet in Microsoft.Extensions.Configuration through the IConfiguration abstraction and is very easy setup.

With the following configuration in the appsettings.Json file

{
    "wakePhrase": "Hello World"
}

The following is a basic example of accessing it from a Console App

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

Console.WriteLine(configuration["wakePhrase"]);

It also works quite well for complex configuration

Consider the following Class

public class WakeOptions
{
    public string WakePhrase {get; set;}
    public string AlternatePhrase {get; set;}
}

with the appsettings.json file

{
  "wakeSettings": {
    "WakePhrase": "Hello World",
    "AlternatePhrase": "Hallo Welt, wie geht's du"
  }
}

When the following code is run in a Console App

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var opts = configuration.GetSection("wakeSettings").Get<WakeOptions>();

Console.WriteLine(opts.WakePhrase);
Console.WriteLine(opts.AlternatePhrase);

The following is output in the console

Hello World
Hallo Welt, wie geht's du

Configuration Providers

Microsoft Configuration has a number of configuration providers, each Configuration Source will have a provider type that enables the ingestion of different locations. Some of the most common providers are as follows:

  • Azure App Configuration Provider : For connecting to Azure configuration management service.
  • Azure Key Vault Provider : For connecting to Azure Key Vault.
  • Environment Variables provider : For using Environment Variables as configuration.
  • File configuration provider : For JSON, XML, and INI files.
  • Memory provider : For in-memory collections.
  • App Secrets provider : A Json file on development machines that assists with keeping secrets on dev machines seamlessly.

Configuration Sources

By default the AspNet web application builder adds the following sources (in order from highest to lowest priority):

  1. Environmental Variables starting with the ASPNETCORE_ prefix using the Non-prefixed environment variables configuration provider.
  2. Environmental Variables starting with the DOTNET_ prefix using the Non-prefixed environment variables configuration provider.
  3. appsettings.json using the JSON configuration provider.
  4. appsettings.{Environment}.json file (this is an optional include) using the JSON configuration provider.
  5. User secrets (when running in Development mode)
  6. Non-Prefixed environment variables using the Non-prefixed environment variables configuration provider.

As you can see below there are 8 sources added by default, the in-memory sources are used by AspNet core to inject specific configuration while building such as Content root path. Web Application Builder default Sources

Any additional providers that are specified are added in the order provided. Order is Important and configuration in sources with a higher priority with take precedence over ones with lower, consider the following:

JSON source with lower priority

{
  "wakeSettings": {
    "WakePhrase": "Hello World",
    "AlternatePhrase": "Hallo Welt, wie geht's du"
  }
}

JSON source with higher priority

{
  "wakeSettings": {
    "AlternatePhrase": "Hey mate, how are you going?"
  }
}

Will result in the equivalent of the following.

{
  "wakeSettings": {
    "WakePhrase": "Hello World",
    "AlternatePhrase": "Hey mate, how are you going?"
  }
}

This is because the WakePhrase is not set in the source with the highest priority so it falls back to the source that it is set in.

For more information on Configuration in .Net Click Here

Azure App Configuration

Azure App Configuration is a cloud based configuration provider in the Azure suite, it integrations seamlessly with the dotnet configuration package and can be used to centralise configuration management some of the features are as follows:

  • Flexible key representations and mappings
  • Tagging with Labels
  • Enable dynamically updating application configuration without a restart
  • Point-in-time replay of settings
  • Feature management including a dedicated UI
  • Integrates with Azure’s Managed Identity for authentication
  • Encryption in transit and at rest
  • Key Vault Integration making it easy to securely inject secrets into your application

I will cover dynamic configuration and how it works in a later article.

For more information on Azure App Configuration Click Here