Guide for ASP.NET Core configuration with appsettings.json, environment-specific settings, and the options pattern
This skill provides guidance for configuration management in ASP.NET Core applications using appsettings.json, environment-specific settings, user secrets, and the options pattern.
Both projects have appsettings.json:
src/ClaudeStack.Web/appsettings.jsonsrc/ClaudeStack.API/appsettings.jsonStandard structure:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
ASP.NET Core loads configuration in this order (later sources override earlier):
Overrides appsettings.json in Development environment:
src/ClaudeStack.Web/appsettings.Development.jsonsrc/ClaudeStack.API/appsettings.Development.jsonExample:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information"
}
}
}
Set via environment variable:
# Windows
$env:ASPNETCORE_ENVIRONMENT="Development"
# Linux/macOS
export ASPNETCORE_ENVIRONMENT=Development
Check in code:
if (app.Environment.IsDevelopment())
{
app.MapOpenApi(); // Only in development
}
# Create production settings
# src/ClaudeStack.Web/appsettings.Production.json
Automatically loaded when ASPNETCORE_ENVIRONMENT=Production.
In Program.cs (Minimal API):
var builder = WebApplication.CreateBuilder(args);
// Access configuration from builder
var connectionString = builder.Configuration["ConnectionStrings:DefaultConnection"];
var app = builder.Build();
// Access configuration in endpoint
app.MapGet("/config", (IConfiguration config) =>
{
var value = config["MySettings:MyValue"];
return value;
});
In MVC Controller:
public class HomeController : Controller
{
private readonly IConfiguration _configuration;
public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}
public IActionResult Index()
{
var value = _configuration["MySettings:MyValue"];
return View();
}
}
appsettings.json:
{
"Database": {
"ConnectionString": "Server=localhost;Database=MyDb",
"Timeout": 30
}
}
Access:
var connectionString = config["Database:ConnectionString"];
var timeout = config.GetValue<int>("Database:Timeout");
1. Define options class:
public class DatabaseOptions
{
public string ConnectionString { get; set; }
public int Timeout { get; set; }
}
2. Bind configuration:
// In Program.cs
builder.Services.Configure<DatabaseOptions>(
builder.Configuration.GetSection("Database"));
3. Use in services/controllers:
public class UserService
{
private readonly DatabaseOptions _options;
public UserService(IOptions<DatabaseOptions> options)
{
_options = options.Value;
}
public void Connect()
{
var conn = _options.ConnectionString;
}
}
IOptions<T> // Singleton, never reloads
IOptionsSnapshot<T> // Scoped, reloads per request
IOptionsMonitor<T> // Singleton, reloads on change
Use IOptions for most cases. Use IOptionsSnapshot if configuration changes between requests. Use IOptionsMonitor to react to configuration changes in real-time.
User Secrets stores sensitive configuration outside the project directory. Used in Development only.
For:
Not for:
cd src/ClaudeStack.Web
dotnet user-secrets init
Adds <UserSecretsId> to .csproj:
<PropertyGroup>
<UserSecretsId>guid-here</UserSecretsId>
</PropertyGroup>
# Set individual secret
dotnet user-secrets set "ApiKey" "secret-value-123"
# Set nested secret
dotnet user-secrets set "Database:ConnectionString" "Server=..."
# List all secrets
dotnet user-secrets list
# Remove secret
dotnet user-secrets remove "ApiKey"
# Clear all secrets
dotnet user-secrets clear
Same as regular configuration:
var apiKey = builder.Configuration["ApiKey"];
Location:
%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json~/.microsoft/usersecrets/<user_secrets_id>/secrets.json# .gitignore already includes:
appsettings.Development.json # Sometimes
**/secrets.json
Use User Secrets for local development secrets.
Prefer:
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MySettings"));
Over:
var value = builder.Configuration["MySettings:Value"]; // Weakly-typed
builder.Services.AddOptions<DatabaseOptions>()
.Bind(builder.Configuration.GetSection("Database"))
.ValidateDataAnnotations()
.ValidateOnStart();
public class DatabaseOptions
{
[Required]
public string ConnectionString { get; set; }
[Range(1, 300)]
public int Timeout { get; set; }
}
// appsettings.json - defaults
{
"Database": {
"ConnectionString": "Server=localhost"
}
}
// appsettings.Production.json - production overrides
{
"Database": {
"ConnectionString": "Server=prod-server"
}
}
Special section:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyDb"
}
}
Access:
var connStr = builder.Configuration.GetConnectionString("DefaultConnection");
// Simple value
config["MyKey"]
// Nested value
config["Section:SubSection:Key"]
// Typed value
config.GetValue<int>("Section:IntKey")
// Connection string
config.GetConnectionString("DefaultConnection")
// Entire section
var section = config.GetSection("MySection")
// Register
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MySection"));
// Use (constructor injection)
public MyService(IOptions<MyOptions> options)
{
var value = options.Value.Property;
}
dotnet user-secrets init
dotnet user-secrets set "Key" "Value"
dotnet user-secrets list
dotnet user-secrets remove "Key"
dotnet user-secrets clear
# Set environment
$env:ASPNETCORE_ENVIRONMENT="Development"
# Check in code
if (app.Environment.IsDevelopment()) { }
if (app.Environment.IsProduction()) { }
Configuration system is stable across .NET versions. This project uses .NET 10 RC 2 patterns.