.NET Configuration in Depth: A Practical Guide
Master the powerful and flexible configuration system in .NET. Learn how to use appsettings.json, environment variables, user secrets, and the options pattern to build robust applications.
Properly managing configuration is a fundamental aspect of building robust and maintainable applications. .NET provides a powerful, flexible, and extensible configuration system that allows you to read settings from a variety of sources and bind them to strongly-typed objects.
Let's dive into the key components of the .NET configuration system.
The Configuration Providers
.NET uses a layered approach to configuration. It can read settings from multiple sources, and if a setting exists in more than one source, the value from the last provider added wins. This creates a powerful hierarchy.
The default configuration setup in a modern .NET web application (WebApplication.CreateBuilder(args)
) adds the following providers in order:
appsettings.json
appsettings.{Environment}.json
(e.g.,appsettings.Development.json
)- User Secrets (if in the Development environment)
- Environment Variables
- Command-line Arguments
This means that a setting in an environment variable will override a setting in appsettings.json
, which is exactly the behavior you want for deploying applications to different environments.
1. appsettings.json
This is the base for your application's configuration. It's a JSON file where you can define your settings in a hierarchical structure.
Example appsettings.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"AllowedHosts": "*",
"MyApiSettings": {
"ApiKey": "default-key",
"TimeoutSeconds": 30
}
}
To access a nested value, you use a colon (:
) as a separator, for example MyApiSettings:ApiKey
.
2. appsettings.{Environment}.json
This file allows you to override your base settings for a specific environment. For example, you might have a different database connection string in development versus production.
Example appsettings.Development.json
:
{
"MyApiSettings": {
"ApiKey": "development-key-from-json"
}
}
When the application runs in the Development
environment, the value of MyApiSettings:ApiKey
will be development-key-from-json
.
3. User Secrets
During development, you should never store sensitive information like API keys or connection strings in appsettings.json
, as this file is often committed to source control. The Secret Manager tool (user secrets) is the solution for this.
It stores sensitive data in a separate JSON file on your local machine, outside of your project directory. This file is not checked into source control.
To initialize user secrets for a project:
dotnet user-secrets init
To set a secret:
dotnet user-secrets set "MyApiSettings:ApiKey" "my-secret-developer-key"
When running in the Development
environment, this secret value will override any corresponding values from the appsettings.json
files.
4. Environment Variables
Environment variables are the standard way to provide configuration in most hosting environments (like Docker, Kubernetes, or App Service).
To override a hierarchical setting, you typically use a double underscore (__
) as the separator.
Example:
To override MyApiSettings:ApiKey
, you would set an environment variable named MyApiSettings__ApiKey
.
# In macOS/Linux
export MyApiSettings__ApiKey="key-from-environment-variable"
# In Windows PowerShell
$env:MyApiSettings__ApiKey = "key-from-environment-variable"
Because this provider is loaded after the JSON files and user secrets, its value will take precedence.
The Options Pattern: Strongly-Typed Configuration
Reading configuration values directly with IConfiguration
(_config["MyApiSettings:ApiKey"]
) is possible, but it's not type-safe. The recommended approach is the Options Pattern.
Create a POCO (Plain Old C# Object) class that matches the structure of your configuration section.
public class MyApiSettings { public string ApiKey { get; set; } = string.Empty; public int TimeoutSeconds { get; set; } }
Register the configuration section in
Program.cs
.// Program.cs builder.Services.Configure<MyApiSettings>(builder.Configuration.GetSection("MyApiSettings"));
Inject
IOptions<T>
into your services or controllers using dependency injection.public class MyService { private readonly MyApiSettings _settings; // Use IOptions<T> to inject the settings public MyService(IOptions<MyApiSettings> apiSettings) { _settings = apiSettings.Value; } public void DoSomething() { string apiKey = _settings.ApiKey; int timeout = _settings.TimeoutSeconds; // ... } }
This approach is strongly typed, follows the principle of dependency injection, and makes your code much cleaner and easier to test.
Conclusion
The .NET configuration system is a robust and essential feature of the platform. By understanding the hierarchy of configuration providers and leveraging the Options Pattern for strongly-typed access, you can build applications that are easy to configure, secure, and maintain across multiple environments.