100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET

Configuration in .NET

How .NET Core layers configuration providers into one IConfiguration and binds settings to strongly typed, validated options.

Building BlocksIntermediate10 min readJul 10, 2026
Analogies

The Layered Configuration Model

The .NET Core configuration system builds a single IConfiguration by layering multiple providers in a defined order — typically appsettings.json first, then appsettings.{Environment}.json, then user secrets in Development, then environment variables, then command-line arguments — where each later provider can override a key set by an earlier one. This means the same key, like "ConnectionStrings:Default", can come from a checked-in JSON file on your laptop but be overridden by an environment variable in a container in production, without any code change, because the providers are merged into one flat key-value view keyed by a colon-delimited path.

🏏

Cricket analogy: This is like a batting order that can be overridden late — the coach sets an initial lineup (appsettings.json) but the captain can bump a specialist finisher up the order (environment variable override) right before the toss if conditions demand it, with the later call always taking precedence, mirroring how later configuration providers override earlier ones.

Binding Configuration to Strongly Typed Options

Rather than sprinkling magic string lookups like configuration["Smtp:Host"] throughout the codebase, the Options pattern binds a configuration section to a plain C# class using services.Configure<SmtpOptions>(configuration.GetSection("Smtp")), then injects IOptions<SmtpOptions>, IOptionsSnapshot<SmtpOptions>, or IOptionsMonitor<SmtpOptions> depending on whether you need a singleton snapshot, a per-request refresh, or live change notification when the underlying file changes. This gives you compile-time checking of property names and types, and centralizes validation (via IValidateOptions<T> or DataAnnotations) in one place instead of scattering ad-hoc parsing and null checks everywhere a raw config value is read.

🏏

Cricket analogy: This is like converting a loosely scribbled team sheet on a napkin into an official, validated scorecard format the ICC recognizes, where every field (batting order, playing XI count) is checked for correctness before the match starts, mirroring how the Options pattern validates and strongly types raw configuration into a checked C# class.

csharp
public class SmtpOptions
{
    public const string SectionName = "Smtp";
    public string Host { get; set; } = string.Empty;
    public int Port { get; set; } = 587;
    public bool UseSsl { get; set; } = true;
}

builder.Services.AddOptions<SmtpOptions>()
    .Bind(builder.Configuration.GetSection(SmtpOptions.SectionName))
    .ValidateDataAnnotations()
    .ValidateOnStart();

public class EmailSender(IOptionsMonitor<SmtpOptions> options)
{
    public void Send(string to, string body)
    {
        var settings = options.CurrentValue;
        // connect to settings.Host:settings.Port ...
    }
}

Secrets and Environment-Specific Values

Sensitive values like connection strings and API keys should never be committed to appsettings.json in source control. During local development, the Secret Manager tool (dotnet user-secrets set "Smtp:Password" "...") stores them outside the repo in a per-project JSON file under your user profile, which the UserSecretsId in the .csproj wires up as an additional configuration provider automatically in Development. In production, the equivalent role is filled by environment variables injected by the orchestrator, or a dedicated secrets store like Azure Key Vault or AWS Secrets Manager, both of which have configuration provider packages that plug into the same IConfiguration pipeline transparently.

🏏

Cricket analogy: This is like a team keeping its actual bowling strategy for a rival out of the public press conference (source control) entirely, sharing it only through a secure internal briefing (Secret Manager) before the match, so no leak reaches the opposition's analysts, mirroring how secrets stay out of appsettings.json and flow through a separate, non-committed channel.

IOptionsMonitor<T> raises a callback via OnChange when the underlying appsettings.json file changes on disk (because JSON file providers set reloadOnChange: true by default), letting long-running services like background workers pick up new settings without a restart — IOptions<T>, by contrast, is resolved once and never updates.

A UserSecretsId in a .csproj only protects secrets from being committed to your own repo during local development — it is not encryption and provides no protection in production. Never rely on user-secrets or plain environment variables for high-sensitivity production secrets; use a managed secrets store with access auditing.

  • IConfiguration merges multiple providers into one key-value view, with later-registered providers overriding earlier ones.
  • The default order is roughly appsettings.json, appsettings.{Environment}.json, user secrets (Development only), environment variables, then command-line args.
  • The Options pattern binds a configuration section to a strongly typed C# class instead of scattering raw string lookups.
  • IOptions<T> is a fixed singleton snapshot; IOptionsSnapshot<T> refreshes per scope; IOptionsMonitor<T> supports live change notifications.
  • ValidateDataAnnotations and ValidateOnStart catch invalid configuration at startup instead of at first use.
  • Secrets should never be committed to appsettings.json; use Secret Manager locally and a managed vault in production.
  • Azure Key Vault and AWS Secrets Manager plug into IConfiguration as additional providers, keeping code unaware of where secrets actually come from.

Practice what you learned

Was this page helpful?

Topics covered

#NET#NETCoreStudyNotes#MicrosoftTechnologies#ConfigurationInNET#Configuration#Layered#Model#Binding#StudyNotes#SkillVeris