IConfiguration, Options pattern, appsettings layering, user secrets, environment variables, and ValidateOnStart. Trigger: configuration, options, appsettings, secrets, IOptions, environment.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
ValidateOnStart() for fail-fast behaviorappsettings.json < appsettings.{Environment}.json < env vars < user secretsappsettings.json — use user secrets (dev) or Key Vault (prod)IOptions<T> for singleton config, IOptionsSnapshot<T> for scoped reloadingpublic sealed class DatabaseOptions
{
public const string SectionName = "Database";
[Required]
public required string ConnectionString { get; init; }
[Range(1, 100)]
public int MaxRetryCount { get; init; } = 3;
[Range(1, 3600)]
public int CommandTimeoutSeconds { get; init; } = 30;
}
public sealed class JwtOptions
{
public const string SectionName = "Jwt";
[Required]
public required string Issuer { get; init; }
[Required]
public required string Audience { get; init; }
[Required, MinLength(32)]
public required string Key { get; init; }
[Range(1, 1440)]
public int ExpiryMinutes { get; init; } = 60;
}
// Program.cs — fail fast if configuration is invalid
builder.Services.AddOptions<DatabaseOptions>()
.BindConfiguration(DatabaseOptions.SectionName)
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<JwtOptions>()
.BindConfiguration(JwtOptions.SectionName)
.ValidateDataAnnotations()
.ValidateOnStart();
public sealed class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
public ValidateOptionsResult Validate(string? name, DatabaseOptions options)
{
var failures = new List<string>();
if (options.ConnectionString.Contains("password=",
StringComparison.OrdinalIgnoreCase)
&& !options.ConnectionString.Contains("Encrypt=true",
StringComparison.OrdinalIgnoreCase))
{
failures.Add(
"Connections with passwords must use Encrypt=true.");
}
return failures.Count > 0
? ValidateOptionsResult.Fail(failures)
: ValidateOptionsResult.Success;
}
}
// Register the validator
builder.Services.AddSingleton<
IValidateOptions<DatabaseOptions>, DatabaseOptionsValidator>();
// Singleton — reads once at startup, never changes
public sealed class StartupService(IOptions<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// Scoped — reloads per request when config changes (requires reloadOnChange)
public sealed class RequestService(IOptionsSnapshot<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// Singleton — reloads and notifies on change
public sealed class MonitorService(IOptionsMonitor<DatabaseOptions> options)
{
public MonitorService(IOptionsMonitor<DatabaseOptions> options)
{
options.OnChange(newOptions =>
{
// React to configuration changes
});
}
}
// appsettings.json — shared defaults
{
"Database": {
"MaxRetryCount": 3,
"CommandTimeoutSeconds": 30
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
// appsettings.Development.json — dev overrides
{
"Database": {
"ConnectionString": "Server=localhost;Database={Domain}Db;Trusted_Connection=true;TrustServerCertificate=true"
}
}
# Initialize user secrets
dotnet user-secrets init
# Set secrets
dotnet user-secrets set "Database:ConnectionString" "Server=localhost;..."
dotnet user-secrets set "Jwt:Key" "your-development-secret-key-min-32-chars"
# Environment variables use __ as section separator
Database__ConnectionString="Server=prod-server;..."
Jwt__Key="production-secret-key"
// Multiple instances of the same options type
builder.Services.AddOptions<StorageOptions>("azure")
.BindConfiguration("Storage:Azure")
.ValidateDataAnnotations();
builder.Services.AddOptions<StorageOptions>("aws")
.BindConfiguration("Storage:Aws")
.ValidateDataAnnotations();
// Consuming named options
public sealed class StorageFactory(IOptionsSnapshot<StorageOptions> options)
{
public IStorageClient Create(string provider)
{
var config = options.Get(provider);
return provider switch
{
"azure" => new AzureBlobClient(config),
"aws" => new S3Client(config),
_ => throw new ArgumentException($"Unknown provider: {provider}")
};
}
}
// BAD: Reading IConfiguration directly — not strongly typed
var connStr = configuration["Database:ConnectionString"];
// BAD: Secrets in appsettings.json
{
"Jwt": { "Key": "super-secret-key-DO-NOT-COMMIT" }
}
// BAD: No validation — app fails at runtime with cryptic errors
services.Configure<DatabaseOptions>(
configuration.GetSection("Database")); // no ValidateOnStart
// BAD: Using IOptions<T> when you need config reload
public class ReloadableService(IOptions<FeatureFlags> options) { }
// Should use IOptionsSnapshot or IOptionsMonitor
IOptions<, IOptionsSnapshot<, IOptionsMonitor< in constructorsBindConfiguration or Bind calls in Program.csValidateOnStart or ValidateDataAnnotations callsappsettings.json for configuration sectionsIConfiguration reads (configuration["Key"])UserSecretsId in .csproj (user secrets enabled)IOptions<T> injectionValidateOnStart()dotnet user-secrets init then set values| Scenario | Interface | Notes |
|---|---|---|
| Singleton service needs config | IOptions<T> | Reads once |
| Scoped service needs live config | IOptionsSnapshot<T> | Reloads per scope |
| React to config changes | IOptionsMonitor<T> | Change callbacks |
| Multiple named configs | IOptionsSnapshot<T> with .Get(name) | Named options |
| Startup validation | ValidateOnStart() | Fail-fast |