Configuration in C# — Complete Guide
In this tutorial, you will learn about Configuration in C#. We cover key concepts, practical examples, and best practices to help you master this topic.
Hook
Applications need to behave differently in development, staging, and production without code changes. The .NET configuration system provides a unified way to read settings from JSON files, environment variables, command-line arguments, and custom providers, all through the IConfiguration abstraction.
Learning Path
graph LR A[Configuration] --> B[appsettings.json] A --> C[Environment Variables] A --> D[Options Pattern] B --> E[Configuration Hierarchy] D --> F[IOptions] style A fill:#4a90d9,color:#fff style B fill:#4a90d9,color:#fff style C fill:#4a90d9,color:#fff style D fill:#4a90d9,color:#fff style E fill:#4a90d9,color:#fff style F fill:#4a90d9,color:#fff
The Configuration Hierarchy
C# configuration follows a layered approach where later sources override earlier ones.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json",
optional: true)
.AddEnvironmentVariables()
.AddCommandLine(args);
IConfigurationRoot configuration = builder.Build();
Reading Configuration Values
Access configuration through the IConfiguration interface.
// appsettings.json
{
"AppSettings": {
"ApplicationName": "MyApp",
"Version": "1.0.0",
"MaxItems": 100,
"FeatureFlags": {
"EnableNewUI": true,
"UseCache": false
}
}
}
// Reading values
string appName = configuration["AppSettings:ApplicationName"];
string version = configuration.GetValue<string>("AppSettings:Version");
int maxItems = configuration.GetValue<int>("AppSettings:MaxItems");
bool enableNewUi = configuration.GetValue<bool>("AppSettings:FeatureFlags:EnableNewUI");
Console.WriteLine($"{appName} v{version}, MaxItems: {maxItems}, NewUI: {enableNewUi}");
Output:
MyApp v1.0.0, MaxItems: 100, NewUI: True
The Options Pattern
Bind configuration sections to strongly-typed classes using the options pattern.
public class AppSettings
{
public string ApplicationName { get; set; } = "";
public string Version { get; set; } = "";
public int MaxItems { get; set; }
public FeatureFlags FeatureFlags { get; set; } = new();
}
public class FeatureFlags
{
public bool EnableNewUI { get; set; }
public bool UseCache { get; set; }
}
// In Startup / Program.cs
var services = new ServiceCollection();
services.Configure<AppSettings>(
configuration.GetSection("AppSettings"));
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptions<AppSettings>>();
AppSettings settings = options.Value;
Console.WriteLine($"App: {settings.ApplicationName}, Version: {settings.Version}");
IOptions, IOptionsSnapshot, and IOptionsMonitor
Choose the right options interface for your needs.
// IOptions<T>: Singleton, reads configuration once at registration
public class MyService
{
private readonly AppSettings _settings;
public MyService(IOptions<AppSettings> options)
{
_settings = options.Value; // Fixed for lifetime
}
}
// IOptionsSnapshot<T>: Scoped, reads per request (reloads when changed)
public class MyScopedService
{
private readonly AppSettings _settings;
public MyScopedService(IOptionsSnapshot<AppSettings> options)
{
_settings = options.Value; // Fresh per scope
}
}
// IOptionsMonitor<T>: Singleton, reads current value (reloads on change)
public class MyMonitoredService
{
private readonly IOptionsMonitor<AppSettings> _monitor;
public MyMonitoredService(IOptionsMonitor<AppSettings> monitor)
{
_monitor = monitor;
}
public void ShowSettings()
{
AppSettings current = _monitor.CurrentValue;
Console.WriteLine($"Current version: {current.Version}");
}
}
Environment Variables
Override settings with environment variables using a colon delimiter (or double underscore on Linux).
export AppSettings__ApplicationName="MyProductionApp"
export AppSettings__MaxItems=200
string appName = configuration["AppSettings:ApplicationName"];
Console.WriteLine($"From env: {appName}");
Custom Configuration Providers
Create custom configuration sources for databases, secrets, or APIs.
public class DatabaseConfigurationSource : IConfigurationSource
{
private readonly string _connectionString;
public DatabaseConfigurationSource(string connectionString)
{
_connectionString = connectionString;
}
public IConfigurationProvider Build(IConfigurationBuilder builder) =>
new DatabaseConfigurationProvider(_connectionString);
}
public class DatabaseConfigurationProvider : ConfigurationProvider
{
private readonly string _connectionString;
public DatabaseConfigurationProvider(string connectionString)
{
_connectionString = connectionString;
}
public override void Load()
{
// Load settings from database
Data = new Dictionary<string, string?>
{
["DatabaseSettings:Timeout"] = "30",
["DatabaseSettings:RetryCount"] = "3"
};
}
}
User Secrets
For development, use the Secret Manager tool to store sensitive data.
dotnet user-secrets init
dotnet user-secrets set "DbPassword" "dev-password-123"
builder.AddUserSecrets<Program>();
Common Mistakes
Hardcoding configuration keys: Use strongly-typed options classes instead of magic string keys throughout the codebase.
Not using reloadOnChange: When
reloadOnChange: true, the configuration system watches for file changes and updatesIOptionsSnapshotandIOptionsMonitorautomatically.Storing secrets in appsettings.json: Use User Secrets in development and Azure Key Vault, HashiCorp Vault, or environment variables in production.
Forgetting environment-specific files: Use
appsettings.Development.json,appsettings.Staging.json, andappsettings.Production.jsonfor environment-specific overrides.Mixing case in configuration keys: Configuration keys are case-insensitive by default in .NET, but environment variables on Linux are case-sensitive. Use consistent casing.
Practice Questions
Create a configuration class for email settings (SMTP host, port, username, password) and bind it from appsettings.json.
Implement a feature flag system using configuration that can toggle features on and off at runtime.
Write a custom configuration provider that reads settings from a REST API endpoint.
Challenge: Build a configuration validation system using DataAnnotations attributes on options classes.
FAQ
{{< faq "Can I use JSON arrays in configuration?" "Yes. Use GetSection to access arrays: configuration.GetSection("AllowedHosts").Get<string[]>()" >}}
{{< faq "How do I handle hierarchical configuration keys?" "Use a colon (:) as the separator: "Parent:Child:Property". On Linux, use a double underscore (__) for environment variables." >}}
Mini Project: Feature Flag System
Build a simple feature flag system using configuration.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
public class FeatureFlags
{
public bool NewCheckoutFlow { get; set; }
public bool DarkMode { get; set; }
public bool BetaSearch { get; set; }
public int MaxRetryCount { get; set; } = 3;
}
public class CheckoutService
{
private readonly IOptionsMonitor<FeatureFlags> _flags;
public CheckoutService(IOptionsMonitor<FeatureFlags> flags)
{
_flags = flags;
}
public void Checkout()
{
if (_flags.CurrentValue.NewCheckoutFlow)
{
Console.WriteLine("Using new checkout flow with improved UX");
}
else
{
Console.WriteLine("Using legacy checkout flow");
}
}
}
// appsettings.json
// {
// "FeatureFlags": {
// "NewCheckoutFlow": true,
// "DarkMode": false,
// "BetaSearch": true
// }
// }
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var services = new ServiceCollection();
services.Configure<FeatureFlags>(builder.GetSection("FeatureFlags"));
services.AddTransient<CheckoutService>();
var provider = services.BuildServiceProvider();
var checkout = provider.GetRequiredService<CheckoutService>();
checkout.Checkout();
Output:
Using new checkout flow with improved UX
The .NET configuration system is a powerful, flexible foundation for managing application settings. Combined with the options pattern, your C# applications can adapt to different environments without code changes or redeployments.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro