[{"data":1,"prerenderedAt":1805},["ShallowReactive",2],{"blog-\u002Fblog\u002Fdotnet-aspnet-core-configuration":3},{"id":4,"title":5,"body":6,"description":1791,"difficulty":1792,"extension":1793,"framework":1794,"frameworkSlug":1013,"meta":1795,"navigation":87,"order":66,"path":1796,"qaPath":1797,"seo":1798,"stem":1799,"subtopic":1800,"topic":1801,"topicSlug":1802,"updated":1803,"__hash__":1804},"blog\u002Fblog\u002Fdotnet-aspnet-core-configuration.md","ASP.NET Core Configuration in Depth",{"type":7,"value":8,"toc":1777},"minimark",[9,14,18,22,35,158,164,255,262,266,372,397,400,404,500,516,520,523,609,619,765,838,842,849,968,983,987,995,1129,1140,1149,1169,1176,1180,1190,1349,1393,1405,1409,1412,1488,1498,1502,1512,1568,1572,1575,1750,1754,1773],[10,11,13],"h2",{"id":12},"why-configuration-knowledge-is-interview-gold","Why configuration knowledge is interview gold",[15,16,17],"p",{},"Configuration management is where many production incidents originate — wrong connection\nstrings, missing API keys, config drift between environments, or features that mysteriously\nstop working because a value changed mid-flight. Interviewers test this topic to gauge\nwhether a developer knows how to make configuration safe, validated, and environment-aware.",[10,19,21],{"id":20},"how-the-configuration-system-works","How the configuration system works",[15,23,24,25,29,30,34],{},"ASP.NET Core aggregates key-value pairs from multiple ",[26,27,28],"strong",{},"configuration providers"," into a\nunified ",[31,32,33],"code",{},"IConfiguration"," interface. Later providers override earlier ones for the same key.",[36,37,42],"pre",{"className":38,"code":39,"language":40,"meta":41,"style":41},"language-csharp shiki shiki-themes github-light github-dark","\u002F\u002F Default provider stack (WebApplication.CreateBuilder):\n\u002F\u002F 1. appsettings.json\n\u002F\u002F 2. appsettings.{Environment}.json   ← overrides 1\n\u002F\u002F 3. User Secrets (Development only)  ← overrides 1+2\n\u002F\u002F 4. Environment variables            ← overrides everything below\n\u002F\u002F 5. Command-line arguments           ← highest priority\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F Read a simple value:\nstring? dbConn = builder.Configuration[\"ConnectionStrings:DefaultConnection\"];\n\n\u002F\u002F GetConnectionString is a shortcut:\nstring? dbConn2 = builder.Configuration.GetConnectionString(\"DefaultConnection\");\n\n\u002F\u002F GetSection returns a sub-node:\nIConfigurationSection mail = builder.Configuration.GetSection(\"Mail\");\nstring? host = mail[\"Host\"];\nint port     = mail.GetValue\u003Cint>(\"Port\", defaultValue: 587);\n","csharp","",[31,43,44,52,58,64,70,76,82,89,95,100,106,112,117,123,129,134,140,146,152],{"__ignoreMap":41},[45,46,49],"span",{"class":47,"line":48},"line",1,[45,50,51],{},"\u002F\u002F Default provider stack (WebApplication.CreateBuilder):\n",[45,53,55],{"class":47,"line":54},2,[45,56,57],{},"\u002F\u002F 1. appsettings.json\n",[45,59,61],{"class":47,"line":60},3,[45,62,63],{},"\u002F\u002F 2. appsettings.{Environment}.json   ← overrides 1\n",[45,65,67],{"class":47,"line":66},4,[45,68,69],{},"\u002F\u002F 3. User Secrets (Development only)  ← overrides 1+2\n",[45,71,73],{"class":47,"line":72},5,[45,74,75],{},"\u002F\u002F 4. Environment variables            ← overrides everything below\n",[45,77,79],{"class":47,"line":78},6,[45,80,81],{},"\u002F\u002F 5. Command-line arguments           ← highest priority\n",[45,83,85],{"class":47,"line":84},7,[45,86,88],{"emptyLinePlaceholder":87},true,"\n",[45,90,92],{"class":47,"line":91},8,[45,93,94],{},"var builder = WebApplication.CreateBuilder(args);\n",[45,96,98],{"class":47,"line":97},9,[45,99,88],{"emptyLinePlaceholder":87},[45,101,103],{"class":47,"line":102},10,[45,104,105],{},"\u002F\u002F Read a simple value:\n",[45,107,109],{"class":47,"line":108},11,[45,110,111],{},"string? dbConn = builder.Configuration[\"ConnectionStrings:DefaultConnection\"];\n",[45,113,115],{"class":47,"line":114},12,[45,116,88],{"emptyLinePlaceholder":87},[45,118,120],{"class":47,"line":119},13,[45,121,122],{},"\u002F\u002F GetConnectionString is a shortcut:\n",[45,124,126],{"class":47,"line":125},14,[45,127,128],{},"string? dbConn2 = builder.Configuration.GetConnectionString(\"DefaultConnection\");\n",[45,130,132],{"class":47,"line":131},15,[45,133,88],{"emptyLinePlaceholder":87},[45,135,137],{"class":47,"line":136},16,[45,138,139],{},"\u002F\u002F GetSection returns a sub-node:\n",[45,141,143],{"class":47,"line":142},17,[45,144,145],{},"IConfigurationSection mail = builder.Configuration.GetSection(\"Mail\");\n",[45,147,149],{"class":47,"line":148},18,[45,150,151],{},"string? host = mail[\"Host\"];\n",[45,153,155],{"class":47,"line":154},19,[45,156,157],{},"int port     = mail.GetValue\u003Cint>(\"Port\", defaultValue: 587);\n",[15,159,160,163],{},[31,161,162],{},"appsettings.json",":",[36,165,169],{"className":166,"code":167,"language":168,"meta":41,"style":41},"language-json shiki shiki-themes github-light github-dark","{\n  \"ConnectionStrings\": {\n    \"DefaultConnection\": \"Server=.;Database=MyDb;Trusted_Connection=True\"\n  },\n  \"Mail\": {\n    \"Host\": \"smtp.example.com\",\n    \"Port\": 587,\n    \"UseSsl\": true\n  }\n}\n","json",[31,170,171,177,186,198,203,210,223,235,245,250],{"__ignoreMap":41},[45,172,173],{"class":47,"line":48},[45,174,176],{"class":175},"sVt8B","{\n",[45,178,179,183],{"class":47,"line":54},[45,180,182],{"class":181},"sj4cs","  \"ConnectionStrings\"",[45,184,185],{"class":175},": {\n",[45,187,188,191,194],{"class":47,"line":60},[45,189,190],{"class":181},"    \"DefaultConnection\"",[45,192,193],{"class":175},": ",[45,195,197],{"class":196},"sZZnC","\"Server=.;Database=MyDb;Trusted_Connection=True\"\n",[45,199,200],{"class":47,"line":66},[45,201,202],{"class":175},"  },\n",[45,204,205,208],{"class":47,"line":72},[45,206,207],{"class":181},"  \"Mail\"",[45,209,185],{"class":175},[45,211,212,215,217,220],{"class":47,"line":78},[45,213,214],{"class":181},"    \"Host\"",[45,216,193],{"class":175},[45,218,219],{"class":196},"\"smtp.example.com\"",[45,221,222],{"class":175},",\n",[45,224,225,228,230,233],{"class":47,"line":84},[45,226,227],{"class":181},"    \"Port\"",[45,229,193],{"class":175},[45,231,232],{"class":181},"587",[45,234,222],{"class":175},[45,236,237,240,242],{"class":47,"line":91},[45,238,239],{"class":181},"    \"UseSsl\"",[45,241,193],{"class":175},[45,243,244],{"class":181},"true\n",[45,246,247],{"class":47,"line":97},[45,248,249],{"class":175},"  }\n",[45,251,252],{"class":47,"line":102},[45,253,254],{"class":175},"}\n",[15,256,257,258,261],{},"Key rule: ",[26,259,260],{},"never hard-code secrets or environment-specific values in source code",". The\nconfiguration system exists precisely so those values can be overridden without a\ncode change.",[10,263,265],{"id":264},"configuration-providers-understanding-the-priority-stack","Configuration providers — understanding the priority stack",[36,267,269],{"className":38,"code":268,"language":40,"meta":41,"style":41},"\u002F\u002F Customizing the stack:\nbuilder.Host.ConfigureAppConfiguration((ctx, config) =>\n{\n    config.Sources.Clear(); \u002F\u002F start fresh if needed\n\n    config.AddJsonFile(\"appsettings.json\", optional: false, reloadOnChange: true);\n    config.AddJsonFile($\"appsettings.{ctx.HostingEnvironment.EnvironmentName}.json\",\n        optional: true, reloadOnChange: true);\n\n    if (ctx.HostingEnvironment.IsDevelopment())\n        config.AddUserSecrets\u003CProgram>();\n\n    \u002F\u002F Environment variables: double underscore __ maps to :\n    \u002F\u002F MYAPP_Mail__Host → Mail:Host\n    config.AddEnvironmentVariables(prefix: \"MYAPP_\");\n\n    config.AddCommandLine(args);\n\n    \u002F\u002F Azure Key Vault (requires NuGet: Azure.Extensions.AspNetCore.Configuration.Secrets):\n    \u002F\u002F config.AddAzureKeyVault(new Uri(\"https:\u002F\u002Fvault.azure.net\u002F\"), new DefaultAzureCredential());\n});\n",[31,270,271,276,281,285,290,294,299,304,309,313,318,323,327,332,337,342,346,351,355,360,366],{"__ignoreMap":41},[45,272,273],{"class":47,"line":48},[45,274,275],{},"\u002F\u002F Customizing the stack:\n",[45,277,278],{"class":47,"line":54},[45,279,280],{},"builder.Host.ConfigureAppConfiguration((ctx, config) =>\n",[45,282,283],{"class":47,"line":60},[45,284,176],{},[45,286,287],{"class":47,"line":66},[45,288,289],{},"    config.Sources.Clear(); \u002F\u002F start fresh if needed\n",[45,291,292],{"class":47,"line":72},[45,293,88],{"emptyLinePlaceholder":87},[45,295,296],{"class":47,"line":78},[45,297,298],{},"    config.AddJsonFile(\"appsettings.json\", optional: false, reloadOnChange: true);\n",[45,300,301],{"class":47,"line":84},[45,302,303],{},"    config.AddJsonFile($\"appsettings.{ctx.HostingEnvironment.EnvironmentName}.json\",\n",[45,305,306],{"class":47,"line":91},[45,307,308],{},"        optional: true, reloadOnChange: true);\n",[45,310,311],{"class":47,"line":97},[45,312,88],{"emptyLinePlaceholder":87},[45,314,315],{"class":47,"line":102},[45,316,317],{},"    if (ctx.HostingEnvironment.IsDevelopment())\n",[45,319,320],{"class":47,"line":108},[45,321,322],{},"        config.AddUserSecrets\u003CProgram>();\n",[45,324,325],{"class":47,"line":114},[45,326,88],{"emptyLinePlaceholder":87},[45,328,329],{"class":47,"line":119},[45,330,331],{},"    \u002F\u002F Environment variables: double underscore __ maps to :\n",[45,333,334],{"class":47,"line":125},[45,335,336],{},"    \u002F\u002F MYAPP_Mail__Host → Mail:Host\n",[45,338,339],{"class":47,"line":131},[45,340,341],{},"    config.AddEnvironmentVariables(prefix: \"MYAPP_\");\n",[45,343,344],{"class":47,"line":136},[45,345,88],{"emptyLinePlaceholder":87},[45,347,348],{"class":47,"line":142},[45,349,350],{},"    config.AddCommandLine(args);\n",[45,352,353],{"class":47,"line":148},[45,354,88],{"emptyLinePlaceholder":87},[45,356,357],{"class":47,"line":154},[45,358,359],{},"    \u002F\u002F Azure Key Vault (requires NuGet: Azure.Extensions.AspNetCore.Configuration.Secrets):\n",[45,361,363],{"class":47,"line":362},20,[45,364,365],{},"    \u002F\u002F config.AddAzureKeyVault(new Uri(\"https:\u002F\u002Fvault.azure.net\u002F\"), new DefaultAzureCredential());\n",[45,367,369],{"class":47,"line":368},21,[45,370,371],{},"});\n",[15,373,374,375,378,379,381,382,385,386,381,389,392,393,396],{},"Environment variable key separator: ",[31,376,377],{},"__"," (double underscore) maps to ",[31,380,163],{},".\n",[31,383,384],{},"MYAPP_Mail__Host"," → config key ",[31,387,388],{},"Mail:Host",[31,390,391],{},"MYAPP_ConnectionStrings__DefaultConnection"," → ",[31,394,395],{},"ConnectionStrings:DefaultConnection",".",[15,398,399],{},"This means you can override any nested config value with an environment variable on any\nplatform, including Docker containers and Kubernetes.",[10,401,403],{"id":402},"reading-config-bind-gett-and-getvalue","Reading config — Bind, Get\u003CT>, and GetValue",[36,405,407],{"className":38,"code":406,"language":40,"meta":41,"style":41},"\u002F\u002F GetValue\u003CT> — single key with optional default:\nstring name   = config.GetValue\u003Cstring>(\"App:Name\") ?? \"DefaultApp\";\nint maxItems  = config.GetValue\u003Cint>(\"App:MaxItems\", 50);\nbool debugMode = config.GetValue\u003Cbool>(\"App:DebugMode\");\n\n\u002F\u002F GetSection + Get\u003CT> — deserialize a section to a POCO:\nAppOptions opts = config.GetSection(\"App\").Get\u003CAppOptions>()!;\n\n\u002F\u002F Bind — populate an existing object:\nvar opts2 = new AppOptions();\nconfig.GetSection(\"App\").Bind(opts2);\n\n\u002F\u002F Arrays \u002F lists:\nstring[] tags = config.GetSection(\"App:Tags\").Get\u003Cstring[]>()!;\n\n\u002F\u002F Check section existence before binding:\nvar section = config.GetSection(\"OptionalFeature\");\nif (section.Exists())\n    section.Bind(featureOptions);\n",[31,408,409,414,419,424,429,433,438,443,447,452,457,462,466,471,476,480,485,490,495],{"__ignoreMap":41},[45,410,411],{"class":47,"line":48},[45,412,413],{},"\u002F\u002F GetValue\u003CT> — single key with optional default:\n",[45,415,416],{"class":47,"line":54},[45,417,418],{},"string name   = config.GetValue\u003Cstring>(\"App:Name\") ?? \"DefaultApp\";\n",[45,420,421],{"class":47,"line":60},[45,422,423],{},"int maxItems  = config.GetValue\u003Cint>(\"App:MaxItems\", 50);\n",[45,425,426],{"class":47,"line":66},[45,427,428],{},"bool debugMode = config.GetValue\u003Cbool>(\"App:DebugMode\");\n",[45,430,431],{"class":47,"line":72},[45,432,88],{"emptyLinePlaceholder":87},[45,434,435],{"class":47,"line":78},[45,436,437],{},"\u002F\u002F GetSection + Get\u003CT> — deserialize a section to a POCO:\n",[45,439,440],{"class":47,"line":84},[45,441,442],{},"AppOptions opts = config.GetSection(\"App\").Get\u003CAppOptions>()!;\n",[45,444,445],{"class":47,"line":91},[45,446,88],{"emptyLinePlaceholder":87},[45,448,449],{"class":47,"line":97},[45,450,451],{},"\u002F\u002F Bind — populate an existing object:\n",[45,453,454],{"class":47,"line":102},[45,455,456],{},"var opts2 = new AppOptions();\n",[45,458,459],{"class":47,"line":108},[45,460,461],{},"config.GetSection(\"App\").Bind(opts2);\n",[45,463,464],{"class":47,"line":114},[45,465,88],{"emptyLinePlaceholder":87},[45,467,468],{"class":47,"line":119},[45,469,470],{},"\u002F\u002F Arrays \u002F lists:\n",[45,472,473],{"class":47,"line":125},[45,474,475],{},"string[] tags = config.GetSection(\"App:Tags\").Get\u003Cstring[]>()!;\n",[45,477,478],{"class":47,"line":131},[45,479,88],{"emptyLinePlaceholder":87},[45,481,482],{"class":47,"line":136},[45,483,484],{},"\u002F\u002F Check section existence before binding:\n",[45,486,487],{"class":47,"line":142},[45,488,489],{},"var section = config.GetSection(\"OptionalFeature\");\n",[45,491,492],{"class":47,"line":148},[45,493,494],{},"if (section.Exists())\n",[45,496,497],{"class":47,"line":154},[45,498,499],{},"    section.Bind(featureOptions);\n",[15,501,502,505,506,508,509,511,512,515],{},[26,503,504],{},"Prefer the Options pattern"," over calling ",[31,507,33],{}," directly in services — it gives\nyou DI, validation, reload support, and testability. Use ",[31,510,33],{}," directly only in\nstartup code (",[31,513,514],{},"Program.cs",") or in simple console apps.",[10,517,519],{"id":518},"the-options-pattern-ioptions-ioptionssnapshot-ioptionsmonitor","The Options pattern — IOptions, IOptionsSnapshot, IOptionsMonitor",[15,521,522],{},"Bind a config section to a strongly-typed class and inject it via DI:",[36,524,526],{"className":38,"code":525,"language":40,"meta":41,"style":41},"\u002F\u002F POCO — convention: add SectionName constant:\npublic class MailOptions\n{\n    public const string SectionName = \"Mail\";\n    public string Host { get; set; } = default!;\n    public int Port { get; set; } = 587;\n    public bool UseSsl { get; set; } = true;\n    public string FromAddress { get; set; } = default!;\n}\n\n\u002F\u002F Register:\nbuilder.Services.Configure\u003CMailOptions>(\n    builder.Configuration.GetSection(MailOptions.SectionName));\n\n\u002F\u002F .NET 7+ shorthand:\nbuilder.Services.AddOptions\u003CMailOptions>()\n    .BindConfiguration(MailOptions.SectionName);\n",[31,527,528,533,538,542,547,552,557,562,567,571,575,580,585,590,594,599,604],{"__ignoreMap":41},[45,529,530],{"class":47,"line":48},[45,531,532],{},"\u002F\u002F POCO — convention: add SectionName constant:\n",[45,534,535],{"class":47,"line":54},[45,536,537],{},"public class MailOptions\n",[45,539,540],{"class":47,"line":60},[45,541,176],{},[45,543,544],{"class":47,"line":66},[45,545,546],{},"    public const string SectionName = \"Mail\";\n",[45,548,549],{"class":47,"line":72},[45,550,551],{},"    public string Host { get; set; } = default!;\n",[45,553,554],{"class":47,"line":78},[45,555,556],{},"    public int Port { get; set; } = 587;\n",[45,558,559],{"class":47,"line":84},[45,560,561],{},"    public bool UseSsl { get; set; } = true;\n",[45,563,564],{"class":47,"line":91},[45,565,566],{},"    public string FromAddress { get; set; } = default!;\n",[45,568,569],{"class":47,"line":97},[45,570,254],{},[45,572,573],{"class":47,"line":102},[45,574,88],{"emptyLinePlaceholder":87},[45,576,577],{"class":47,"line":108},[45,578,579],{},"\u002F\u002F Register:\n",[45,581,582],{"class":47,"line":114},[45,583,584],{},"builder.Services.Configure\u003CMailOptions>(\n",[45,586,587],{"class":47,"line":119},[45,588,589],{},"    builder.Configuration.GetSection(MailOptions.SectionName));\n",[45,591,592],{"class":47,"line":125},[45,593,88],{"emptyLinePlaceholder":87},[45,595,596],{"class":47,"line":131},[45,597,598],{},"\u002F\u002F .NET 7+ shorthand:\n",[45,600,601],{"class":47,"line":136},[45,602,603],{},"builder.Services.AddOptions\u003CMailOptions>()\n",[45,605,606],{"class":47,"line":142},[45,607,608],{},"    .BindConfiguration(MailOptions.SectionName);\n",[15,610,611,612,615,616,163],{},"The three interfaces differ in ",[26,613,614],{},"lifetime"," and ",[26,617,618],{},"reload behavior",[36,620,622],{"className":38,"code":621,"language":40,"meta":41,"style":41},"\u002F\u002F IOptions\u003CT> — singleton; reads config ONCE at startup:\npublic class EmailService\n{\n    private readonly MailOptions _opts;\n    public EmailService(IOptions\u003CMailOptions> opts) => _opts = opts.Value;\n    \u002F\u002F _opts.Host never changes, even if appsettings.json is edited on disk\n}\n\n\u002F\u002F IOptionsSnapshot\u003CT> — scoped; fresh per HTTP request:\npublic class EmailService\n{\n    private readonly MailOptions _opts;\n    public EmailService(IOptionsSnapshot\u003CMailOptions> opts) => _opts = opts.Value;\n    \u002F\u002F New value takes effect on the next request after a reload\n}\n\n\u002F\u002F IOptionsMonitor\u003CT> — singleton; sees changes immediately:\npublic class EmailService\n{\n    private readonly IOptionsMonitor\u003CMailOptions> _monitor;\n    public EmailService(IOptionsMonitor\u003CMailOptions> monitor)\n    {\n        _monitor = monitor;\n        monitor.OnChange(newOpts =>\n            Console.WriteLine($\"Config changed: Host={newOpts.Host}\"));\n    }\n\n    public string CurrentHost => _monitor.CurrentValue.Host; \u002F\u002F always fresh\n}\n",[31,623,624,629,634,638,643,648,653,657,661,666,670,674,678,683,688,692,696,701,705,709,714,719,725,731,737,743,749,754,760],{"__ignoreMap":41},[45,625,626],{"class":47,"line":48},[45,627,628],{},"\u002F\u002F IOptions\u003CT> — singleton; reads config ONCE at startup:\n",[45,630,631],{"class":47,"line":54},[45,632,633],{},"public class EmailService\n",[45,635,636],{"class":47,"line":60},[45,637,176],{},[45,639,640],{"class":47,"line":66},[45,641,642],{},"    private readonly MailOptions _opts;\n",[45,644,645],{"class":47,"line":72},[45,646,647],{},"    public EmailService(IOptions\u003CMailOptions> opts) => _opts = opts.Value;\n",[45,649,650],{"class":47,"line":78},[45,651,652],{},"    \u002F\u002F _opts.Host never changes, even if appsettings.json is edited on disk\n",[45,654,655],{"class":47,"line":84},[45,656,254],{},[45,658,659],{"class":47,"line":91},[45,660,88],{"emptyLinePlaceholder":87},[45,662,663],{"class":47,"line":97},[45,664,665],{},"\u002F\u002F IOptionsSnapshot\u003CT> — scoped; fresh per HTTP request:\n",[45,667,668],{"class":47,"line":102},[45,669,633],{},[45,671,672],{"class":47,"line":108},[45,673,176],{},[45,675,676],{"class":47,"line":114},[45,677,642],{},[45,679,680],{"class":47,"line":119},[45,681,682],{},"    public EmailService(IOptionsSnapshot\u003CMailOptions> opts) => _opts = opts.Value;\n",[45,684,685],{"class":47,"line":125},[45,686,687],{},"    \u002F\u002F New value takes effect on the next request after a reload\n",[45,689,690],{"class":47,"line":131},[45,691,254],{},[45,693,694],{"class":47,"line":136},[45,695,88],{"emptyLinePlaceholder":87},[45,697,698],{"class":47,"line":142},[45,699,700],{},"\u002F\u002F IOptionsMonitor\u003CT> — singleton; sees changes immediately:\n",[45,702,703],{"class":47,"line":148},[45,704,633],{},[45,706,707],{"class":47,"line":154},[45,708,176],{},[45,710,711],{"class":47,"line":362},[45,712,713],{},"    private readonly IOptionsMonitor\u003CMailOptions> _monitor;\n",[45,715,716],{"class":47,"line":368},[45,717,718],{},"    public EmailService(IOptionsMonitor\u003CMailOptions> monitor)\n",[45,720,722],{"class":47,"line":721},22,[45,723,724],{},"    {\n",[45,726,728],{"class":47,"line":727},23,[45,729,730],{},"        _monitor = monitor;\n",[45,732,734],{"class":47,"line":733},24,[45,735,736],{},"        monitor.OnChange(newOpts =>\n",[45,738,740],{"class":47,"line":739},25,[45,741,742],{},"            Console.WriteLine($\"Config changed: Host={newOpts.Host}\"));\n",[45,744,746],{"class":47,"line":745},26,[45,747,748],{},"    }\n",[45,750,752],{"class":47,"line":751},27,[45,753,88],{"emptyLinePlaceholder":87},[45,755,757],{"class":47,"line":756},28,[45,758,759],{},"    public string CurrentHost => _monitor.CurrentValue.Host; \u002F\u002F always fresh\n",[45,761,763],{"class":47,"line":762},29,[45,764,254],{},[766,767,768,787],"table",{},[769,770,771],"thead",{},[772,773,774,778,781,784],"tr",{},[775,776,777],"th",{},"Interface",[775,779,780],{},"DI Lifetime",[775,782,783],{},"Sees reload?",[775,785,786],{},"Use when",[788,789,790,807,823],"tbody",{},[772,791,792,798,801,804],{},[793,794,795],"td",{},[31,796,797],{},"IOptions\u003CT>",[793,799,800],{},"Singleton",[793,802,803],{},"Never",[793,805,806],{},"Startup-only, singletons",[772,808,809,814,817,820],{},[793,810,811],{},[31,812,813],{},"IOptionsSnapshot\u003CT>",[793,815,816],{},"Scoped",[793,818,819],{},"Per-request",[793,821,822],{},"Web requests, feature flags",[772,824,825,830,832,835],{},[793,826,827],{},[31,828,829],{},"IOptionsMonitor\u003CT>",[793,831,800],{},[793,833,834],{},"Immediately",[793,836,837],{},"Background workers, long-running singletons",[10,839,841],{"id":840},"options-validation-fail-fast-at-startup","Options validation — fail fast at startup",[15,843,844,845,848],{},"Without validation, bad config causes a runtime error when the feature is first used.\n",[31,846,847],{},"ValidateOnStart()"," makes the app refuse to start if config is invalid:",[36,850,852],{"className":38,"code":851,"language":40,"meta":41,"style":41},"public class SmtpOptions\n{\n    [Required]\n    public string Host { get; set; } = default!;\n\n    [Range(1, 65535)]\n    public int Port { get; set; }\n\n    [Required, EmailAddress]\n    public string FromAddress { get; set; } = default!;\n}\n\nbuilder.Services\n    .AddOptions\u003CSmtpOptions>()\n    .BindConfiguration(\"Smtp\")\n    .ValidateDataAnnotations()\n    .ValidateOnStart(); \u002F\u002F throw on startup, not on first use\n\n\u002F\u002F Custom validation logic:\nbuilder.Services\n    .AddOptions\u003CSmtpOptions>()\n    .BindConfiguration(\"Smtp\")\n    .Validate(o => o.Port != 25 || !o.Host.Contains(\".corp\"),\n        \"Port 25 blocked for corp mail servers\")\n    .ValidateOnStart();\n",[31,853,854,859,863,868,872,876,881,886,890,895,899,903,907,912,917,922,927,932,936,941,945,949,953,958,963],{"__ignoreMap":41},[45,855,856],{"class":47,"line":48},[45,857,858],{},"public class SmtpOptions\n",[45,860,861],{"class":47,"line":54},[45,862,176],{},[45,864,865],{"class":47,"line":60},[45,866,867],{},"    [Required]\n",[45,869,870],{"class":47,"line":66},[45,871,551],{},[45,873,874],{"class":47,"line":72},[45,875,88],{"emptyLinePlaceholder":87},[45,877,878],{"class":47,"line":78},[45,879,880],{},"    [Range(1, 65535)]\n",[45,882,883],{"class":47,"line":84},[45,884,885],{},"    public int Port { get; set; }\n",[45,887,888],{"class":47,"line":91},[45,889,88],{"emptyLinePlaceholder":87},[45,891,892],{"class":47,"line":97},[45,893,894],{},"    [Required, EmailAddress]\n",[45,896,897],{"class":47,"line":102},[45,898,566],{},[45,900,901],{"class":47,"line":108},[45,902,254],{},[45,904,905],{"class":47,"line":114},[45,906,88],{"emptyLinePlaceholder":87},[45,908,909],{"class":47,"line":119},[45,910,911],{},"builder.Services\n",[45,913,914],{"class":47,"line":125},[45,915,916],{},"    .AddOptions\u003CSmtpOptions>()\n",[45,918,919],{"class":47,"line":131},[45,920,921],{},"    .BindConfiguration(\"Smtp\")\n",[45,923,924],{"class":47,"line":136},[45,925,926],{},"    .ValidateDataAnnotations()\n",[45,928,929],{"class":47,"line":142},[45,930,931],{},"    .ValidateOnStart(); \u002F\u002F throw on startup, not on first use\n",[45,933,934],{"class":47,"line":148},[45,935,88],{"emptyLinePlaceholder":87},[45,937,938],{"class":47,"line":154},[45,939,940],{},"\u002F\u002F Custom validation logic:\n",[45,942,943],{"class":47,"line":362},[45,944,911],{},[45,946,947],{"class":47,"line":368},[45,948,916],{},[45,950,951],{"class":47,"line":721},[45,952,921],{},[45,954,955],{"class":47,"line":727},[45,956,957],{},"    .Validate(o => o.Port != 25 || !o.Host.Contains(\".corp\"),\n",[45,959,960],{"class":47,"line":733},[45,961,962],{},"        \"Port 25 blocked for corp mail servers\")\n",[45,964,965],{"class":47,"line":739},[45,966,967],{},"    .ValidateOnStart();\n",[15,969,970,971,974,975,978,979,982],{},"A ",[31,972,973],{},"ValidateOnStart"," failure throws ",[31,976,977],{},"OptionsValidationException"," during\n",[31,980,981],{},"app.Run()"," — the app exits cleanly with a clear error message rather than silently\nmisbehaving in production.",[10,984,986],{"id":985},"user-secrets-safe-development-credentials","User Secrets — safe development credentials",[15,988,989,990,994],{},"User Secrets store sensitive config ",[991,992,993],"em",{},"outside"," the repo directory so they are never\naccidentally committed:",[36,996,1000],{"className":997,"code":998,"language":999,"meta":41,"style":41},"language-bash shiki shiki-themes github-light github-dark","# Initialize (adds \u003CUserSecretsId> to .csproj):\ndotnet user-secrets init\n\n# Set values:\ndotnet user-secrets set \"ConnectionStrings:DefaultConnection\" \"Server=dev-db;...\"\ndotnet user-secrets set \"Stripe:SecretKey\" \"sk_test_abc123\"\ndotnet user-secrets set \"Mail:Password\" \"dev-smtp-password\"\n\n# List:\ndotnet user-secrets list\n\n# Remove:\ndotnet user-secrets remove \"Mail:Password\"\n\n# Clear all:\ndotnet user-secrets clear\n","bash",[31,1001,1002,1008,1020,1024,1029,1044,1058,1072,1076,1081,1090,1094,1099,1111,1115,1120],{"__ignoreMap":41},[45,1003,1004],{"class":47,"line":48},[45,1005,1007],{"class":1006},"sJ8bj","# Initialize (adds \u003CUserSecretsId> to .csproj):\n",[45,1009,1010,1014,1017],{"class":47,"line":54},[45,1011,1013],{"class":1012},"sScJk","dotnet",[45,1015,1016],{"class":196}," user-secrets",[45,1018,1019],{"class":196}," init\n",[45,1021,1022],{"class":47,"line":60},[45,1023,88],{"emptyLinePlaceholder":87},[45,1025,1026],{"class":47,"line":66},[45,1027,1028],{"class":1006},"# Set values:\n",[45,1030,1031,1033,1035,1038,1041],{"class":47,"line":72},[45,1032,1013],{"class":1012},[45,1034,1016],{"class":196},[45,1036,1037],{"class":196}," set",[45,1039,1040],{"class":196}," \"ConnectionStrings:DefaultConnection\"",[45,1042,1043],{"class":196}," \"Server=dev-db;...\"\n",[45,1045,1046,1048,1050,1052,1055],{"class":47,"line":78},[45,1047,1013],{"class":1012},[45,1049,1016],{"class":196},[45,1051,1037],{"class":196},[45,1053,1054],{"class":196}," \"Stripe:SecretKey\"",[45,1056,1057],{"class":196}," \"sk_test_abc123\"\n",[45,1059,1060,1062,1064,1066,1069],{"class":47,"line":84},[45,1061,1013],{"class":1012},[45,1063,1016],{"class":196},[45,1065,1037],{"class":196},[45,1067,1068],{"class":196}," \"Mail:Password\"",[45,1070,1071],{"class":196}," \"dev-smtp-password\"\n",[45,1073,1074],{"class":47,"line":91},[45,1075,88],{"emptyLinePlaceholder":87},[45,1077,1078],{"class":47,"line":97},[45,1079,1080],{"class":1006},"# List:\n",[45,1082,1083,1085,1087],{"class":47,"line":102},[45,1084,1013],{"class":1012},[45,1086,1016],{"class":196},[45,1088,1089],{"class":196}," list\n",[45,1091,1092],{"class":47,"line":108},[45,1093,88],{"emptyLinePlaceholder":87},[45,1095,1096],{"class":47,"line":114},[45,1097,1098],{"class":1006},"# Remove:\n",[45,1100,1101,1103,1105,1108],{"class":47,"line":119},[45,1102,1013],{"class":1012},[45,1104,1016],{"class":196},[45,1106,1107],{"class":196}," remove",[45,1109,1110],{"class":196}," \"Mail:Password\"\n",[45,1112,1113],{"class":47,"line":125},[45,1114,88],{"emptyLinePlaceholder":87},[45,1116,1117],{"class":47,"line":131},[45,1118,1119],{"class":1006},"# Clear all:\n",[45,1121,1122,1124,1126],{"class":47,"line":136},[45,1123,1013],{"class":1012},[45,1125,1016],{"class":196},[45,1127,1128],{"class":196}," clear\n",[15,1130,1131,1132,1135,1136,1139],{},"Stored at ",[31,1133,1134],{},"%APPDATA%\\Microsoft\\UserSecrets\\\u003CUserSecretsId>\\secrets.json"," (Windows)\nor ",[31,1137,1138],{},"~\u002F.microsoft\u002Fusersecrets\u002F\u003Cid>\u002Fsecrets.json"," (Linux\u002FmacOS).",[15,1141,1142,1145,1146,163],{},[31,1143,1144],{},"WebApplication.CreateBuilder"," automatically loads them in ",[31,1147,1148],{},"Development",[36,1150,1152],{"className":38,"code":1151,"language":40,"meta":41,"style":41},"\u002F\u002F This is what the builder does automatically in Development:\nif (ctx.HostingEnvironment.IsDevelopment())\n    config.AddUserSecrets\u003CProgram>();\n",[31,1153,1154,1159,1164],{"__ignoreMap":41},[45,1155,1156],{"class":47,"line":48},[45,1157,1158],{},"\u002F\u002F This is what the builder does automatically in Development:\n",[45,1160,1161],{"class":47,"line":54},[45,1162,1163],{},"if (ctx.HostingEnvironment.IsDevelopment())\n",[45,1165,1166],{"class":47,"line":60},[45,1167,1168],{},"    config.AddUserSecrets\u003CProgram>();\n",[15,1170,1171,1172,1175],{},"User Secrets are a ",[26,1173,1174],{},"development convenience only"," — in staging and production, use\nenvironment variables or a secrets manager (Azure Key Vault, AWS Secrets Manager, HashiCorp\nVault).",[10,1177,1179],{"id":1178},"environment-specific-configuration","Environment-specific configuration",[15,1181,1182,1185,1186,1189],{},[31,1183,1184],{},"ASPNETCORE_ENVIRONMENT"," controls which ",[31,1187,1188],{},"appsettings.{Env}.json"," overlay is loaded:",[36,1191,1193],{"className":166,"code":1192,"language":168,"meta":41,"style":41},"\u002F\u002F appsettings.json — base config, in source control, no secrets:\n{\n  \"Logging\": { \"LogLevel\": { \"Default\": \"Warning\" } },\n  \"FeatureFlags\": { \"NewCheckout\": false }\n}\n\n\u002F\u002F appsettings.Development.json — local overrides, can be committed:\n{\n  \"Logging\": { \"LogLevel\": { \"Default\": \"Debug\", \"Microsoft\": \"Information\" } },\n  \"FeatureFlags\": { \"NewCheckout\": true }\n}\n\n\u002F\u002F appsettings.Production.json — minimal; real secrets come from env vars:\n{\n  \"Logging\": { \"LogLevel\": { \"Default\": \"Error\" } }\n}\n",[31,1194,1195,1200,1204,1228,1246,1250,1254,1259,1263,1293,1308,1312,1316,1321,1325,1345],{"__ignoreMap":41},[45,1196,1197],{"class":47,"line":48},[45,1198,1199],{"class":1006},"\u002F\u002F appsettings.json — base config, in source control, no secrets:\n",[45,1201,1202],{"class":47,"line":54},[45,1203,176],{"class":175},[45,1205,1206,1209,1212,1215,1217,1220,1222,1225],{"class":47,"line":60},[45,1207,1208],{"class":181},"  \"Logging\"",[45,1210,1211],{"class":175},": { ",[45,1213,1214],{"class":181},"\"LogLevel\"",[45,1216,1211],{"class":175},[45,1218,1219],{"class":181},"\"Default\"",[45,1221,193],{"class":175},[45,1223,1224],{"class":196},"\"Warning\"",[45,1226,1227],{"class":175}," } },\n",[45,1229,1230,1233,1235,1238,1240,1243],{"class":47,"line":66},[45,1231,1232],{"class":181},"  \"FeatureFlags\"",[45,1234,1211],{"class":175},[45,1236,1237],{"class":181},"\"NewCheckout\"",[45,1239,193],{"class":175},[45,1241,1242],{"class":181},"false",[45,1244,1245],{"class":175}," }\n",[45,1247,1248],{"class":47,"line":72},[45,1249,254],{"class":175},[45,1251,1252],{"class":47,"line":78},[45,1253,88],{"emptyLinePlaceholder":87},[45,1255,1256],{"class":47,"line":84},[45,1257,1258],{"class":1006},"\u002F\u002F appsettings.Development.json — local overrides, can be committed:\n",[45,1260,1261],{"class":47,"line":91},[45,1262,176],{"class":175},[45,1264,1265,1267,1269,1271,1273,1275,1277,1280,1283,1286,1288,1291],{"class":47,"line":97},[45,1266,1208],{"class":181},[45,1268,1211],{"class":175},[45,1270,1214],{"class":181},[45,1272,1211],{"class":175},[45,1274,1219],{"class":181},[45,1276,193],{"class":175},[45,1278,1279],{"class":196},"\"Debug\"",[45,1281,1282],{"class":175},", ",[45,1284,1285],{"class":181},"\"Microsoft\"",[45,1287,193],{"class":175},[45,1289,1290],{"class":196},"\"Information\"",[45,1292,1227],{"class":175},[45,1294,1295,1297,1299,1301,1303,1306],{"class":47,"line":102},[45,1296,1232],{"class":181},[45,1298,1211],{"class":175},[45,1300,1237],{"class":181},[45,1302,193],{"class":175},[45,1304,1305],{"class":181},"true",[45,1307,1245],{"class":175},[45,1309,1310],{"class":47,"line":108},[45,1311,254],{"class":175},[45,1313,1314],{"class":47,"line":114},[45,1315,88],{"emptyLinePlaceholder":87},[45,1317,1318],{"class":47,"line":119},[45,1319,1320],{"class":1006},"\u002F\u002F appsettings.Production.json — minimal; real secrets come from env vars:\n",[45,1322,1323],{"class":47,"line":125},[45,1324,176],{"class":175},[45,1326,1327,1329,1331,1333,1335,1337,1339,1342],{"class":47,"line":131},[45,1328,1208],{"class":181},[45,1330,1211],{"class":175},[45,1332,1214],{"class":181},[45,1334,1211],{"class":175},[45,1336,1219],{"class":181},[45,1338,193],{"class":175},[45,1340,1341],{"class":196},"\"Error\"",[45,1343,1344],{"class":175}," } }\n",[45,1346,1347],{"class":47,"line":136},[45,1348,254],{"class":175},[36,1350,1352],{"className":38,"code":1351,"language":40,"meta":41,"style":41},"\u002F\u002F Check environment in code:\nif (app.Environment.IsDevelopment())\n    app.UseDeveloperExceptionPage();\n\n\u002F\u002F Custom environment names:\n\u002F\u002F ASPNETCORE_ENVIRONMENT=Staging → loads appsettings.Staging.json\nif (app.Environment.IsEnvironment(\"Staging\"))\n    EnableBluegreenRouting();\n",[31,1353,1354,1359,1364,1369,1373,1378,1383,1388],{"__ignoreMap":41},[45,1355,1356],{"class":47,"line":48},[45,1357,1358],{},"\u002F\u002F Check environment in code:\n",[45,1360,1361],{"class":47,"line":54},[45,1362,1363],{},"if (app.Environment.IsDevelopment())\n",[45,1365,1366],{"class":47,"line":60},[45,1367,1368],{},"    app.UseDeveloperExceptionPage();\n",[45,1370,1371],{"class":47,"line":66},[45,1372,88],{"emptyLinePlaceholder":87},[45,1374,1375],{"class":47,"line":72},[45,1376,1377],{},"\u002F\u002F Custom environment names:\n",[45,1379,1380],{"class":47,"line":78},[45,1381,1382],{},"\u002F\u002F ASPNETCORE_ENVIRONMENT=Staging → loads appsettings.Staging.json\n",[45,1384,1385],{"class":47,"line":84},[45,1386,1387],{},"if (app.Environment.IsEnvironment(\"Staging\"))\n",[45,1389,1390],{"class":47,"line":91},[45,1391,1392],{},"    EnableBluegreenRouting();\n",[15,1394,1395,1396,1282,1398,1282,1401,1404],{},"Standard environments: ",[31,1397,1148],{},[31,1399,1400],{},"Staging",[31,1402,1403],{},"Production",". Custom names are allowed.",[10,1406,1408],{"id":1407},"named-options-multiple-instances-of-the-same-type","Named options — multiple instances of the same type",[15,1410,1411],{},"Named options let you configure multiple instances of the same options class:",[36,1413,1415],{"className":38,"code":1414,"language":40,"meta":41,"style":41},"\u002F\u002F Two SMTP configs:\nbuilder.Services.Configure\u003CSmtpOptions>(\"Transactional\",\n    builder.Configuration.GetSection(\"Smtp:Transactional\"));\nbuilder.Services.Configure\u003CSmtpOptions>(\"Marketing\",\n    builder.Configuration.GetSection(\"Smtp:Marketing\"));\n\n\u002F\u002F Inject via IOptionsMonitor — get by name:\npublic class EmailDispatcher\n{\n    private readonly IOptionsMonitor\u003CSmtpOptions> _monitor;\n    public EmailDispatcher(IOptionsMonitor\u003CSmtpOptions> monitor) => _monitor = monitor;\n\n    public SmtpOptions GetTransactional() => _monitor.Get(\"Transactional\");\n    public SmtpOptions GetMarketing()     => _monitor.Get(\"Marketing\");\n}\n",[31,1416,1417,1422,1427,1432,1437,1442,1446,1451,1456,1460,1465,1470,1474,1479,1484],{"__ignoreMap":41},[45,1418,1419],{"class":47,"line":48},[45,1420,1421],{},"\u002F\u002F Two SMTP configs:\n",[45,1423,1424],{"class":47,"line":54},[45,1425,1426],{},"builder.Services.Configure\u003CSmtpOptions>(\"Transactional\",\n",[45,1428,1429],{"class":47,"line":60},[45,1430,1431],{},"    builder.Configuration.GetSection(\"Smtp:Transactional\"));\n",[45,1433,1434],{"class":47,"line":66},[45,1435,1436],{},"builder.Services.Configure\u003CSmtpOptions>(\"Marketing\",\n",[45,1438,1439],{"class":47,"line":72},[45,1440,1441],{},"    builder.Configuration.GetSection(\"Smtp:Marketing\"));\n",[45,1443,1444],{"class":47,"line":78},[45,1445,88],{"emptyLinePlaceholder":87},[45,1447,1448],{"class":47,"line":84},[45,1449,1450],{},"\u002F\u002F Inject via IOptionsMonitor — get by name:\n",[45,1452,1453],{"class":47,"line":91},[45,1454,1455],{},"public class EmailDispatcher\n",[45,1457,1458],{"class":47,"line":97},[45,1459,176],{},[45,1461,1462],{"class":47,"line":102},[45,1463,1464],{},"    private readonly IOptionsMonitor\u003CSmtpOptions> _monitor;\n",[45,1466,1467],{"class":47,"line":108},[45,1468,1469],{},"    public EmailDispatcher(IOptionsMonitor\u003CSmtpOptions> monitor) => _monitor = monitor;\n",[45,1471,1472],{"class":47,"line":114},[45,1473,88],{"emptyLinePlaceholder":87},[45,1475,1476],{"class":47,"line":119},[45,1477,1478],{},"    public SmtpOptions GetTransactional() => _monitor.Get(\"Transactional\");\n",[45,1480,1481],{"class":47,"line":125},[45,1482,1483],{},"    public SmtpOptions GetMarketing()     => _monitor.Get(\"Marketing\");\n",[45,1485,1486],{"class":47,"line":131},[45,1487,254],{},[15,1489,1490,1493,1494,1497],{},[31,1491,1492],{},"IHttpClientFactory"," uses named options internally — each ",[31,1495,1496],{},"AddHttpClient(\"name\", ...)"," is\na named options configuration under the hood.",[10,1499,1501],{"id":1500},"postconfigure-guaranteed-last-word","PostConfigure — guaranteed last word",[15,1503,1504,1507,1508,1511],{},[31,1505,1506],{},"PostConfigure"," runs after all ",[31,1509,1510],{},"Configure"," calls for the same type, making it useful for\nenforcing global constraints regardless of what third-party libraries set:",[36,1513,1515],{"className":38,"code":1514,"language":40,"meta":41,"style":41},"\u002F\u002F Third-party library:\nbuilder.Services.Configure\u003CCacheOptions>(o => { o.MaxSize = 1000; o.Expiry = TimeSpan.FromMinutes(5); });\n\n\u002F\u002F Your constraint — always runs last:\nbuilder.Services.PostConfigure\u003CCacheOptions>(o =>\n{\n    if (o.MaxSize > 500) o.MaxSize = 500;  \u002F\u002F cap regardless of library setting\n});\n\n\u002F\u002F PostConfigureAll — applies to ALL named instances:\nbuilder.Services.PostConfigureAll\u003CSmtpOptions>(o => o.UseSsl = true); \u002F\u002F enforce SSL everywhere\n",[31,1516,1517,1522,1527,1531,1536,1541,1545,1550,1554,1558,1563],{"__ignoreMap":41},[45,1518,1519],{"class":47,"line":48},[45,1520,1521],{},"\u002F\u002F Third-party library:\n",[45,1523,1524],{"class":47,"line":54},[45,1525,1526],{},"builder.Services.Configure\u003CCacheOptions>(o => { o.MaxSize = 1000; o.Expiry = TimeSpan.FromMinutes(5); });\n",[45,1528,1529],{"class":47,"line":60},[45,1530,88],{"emptyLinePlaceholder":87},[45,1532,1533],{"class":47,"line":66},[45,1534,1535],{},"\u002F\u002F Your constraint — always runs last:\n",[45,1537,1538],{"class":47,"line":72},[45,1539,1540],{},"builder.Services.PostConfigure\u003CCacheOptions>(o =>\n",[45,1542,1543],{"class":47,"line":78},[45,1544,176],{},[45,1546,1547],{"class":47,"line":84},[45,1548,1549],{},"    if (o.MaxSize > 500) o.MaxSize = 500;  \u002F\u002F cap regardless of library setting\n",[45,1551,1552],{"class":47,"line":91},[45,1553,371],{},[45,1555,1556],{"class":47,"line":97},[45,1557,88],{"emptyLinePlaceholder":87},[45,1559,1560],{"class":47,"line":102},[45,1561,1562],{},"\u002F\u002F PostConfigureAll — applies to ALL named instances:\n",[45,1564,1565],{"class":47,"line":108},[45,1566,1567],{},"builder.Services.PostConfigureAll\u003CSmtpOptions>(o => o.UseSsl = true); \u002F\u002F enforce SSL everywhere\n",[10,1569,1571],{"id":1570},"configuration-in-background-services-and-console-apps","Configuration in background services and console apps",[15,1573,1574],{},"The configuration system is framework-agnostic — it works equally well outside ASP.NET Core:",[36,1576,1578],{"className":38,"code":1577,"language":40,"meta":41,"style":41},"var host = Host.CreateDefaultBuilder(args)\n    .ConfigureAppConfiguration((ctx, config) =>\n    {\n        config.AddJsonFile(\"appsettings.json\");\n        config.AddEnvironmentVariables();\n        if (ctx.HostingEnvironment.IsDevelopment())\n            config.AddUserSecrets\u003CProgram>();\n    })\n    .ConfigureServices((ctx, services) =>\n    {\n        services.AddOptions\u003CWorkerOptions>()\n            .BindConfiguration(\"Worker\")\n            .ValidateDataAnnotations()\n            .ValidateOnStart();\n\n        services.AddHostedService\u003CMyWorker>();\n    })\n    .Build();\n\nawait host.RunAsync();\n\npublic class MyWorker : BackgroundService\n{\n    private readonly WorkerOptions _opts;\n    public MyWorker(IOptions\u003CWorkerOptions> opts) => _opts = opts.Value;\n\n    protected override async Task ExecuteAsync(CancellationToken ct)\n    {\n        while (!ct.IsCancellationRequested)\n        {\n            DoWork();\n            await Task.Delay(TimeSpan.FromSeconds(_opts.PollIntervalSeconds), ct);\n        }\n    }\n}\n",[31,1579,1580,1585,1590,1594,1599,1604,1609,1614,1619,1624,1628,1633,1638,1643,1648,1652,1657,1661,1666,1670,1675,1679,1684,1688,1693,1698,1702,1707,1711,1716,1722,1728,1734,1740,1745],{"__ignoreMap":41},[45,1581,1582],{"class":47,"line":48},[45,1583,1584],{},"var host = Host.CreateDefaultBuilder(args)\n",[45,1586,1587],{"class":47,"line":54},[45,1588,1589],{},"    .ConfigureAppConfiguration((ctx, config) =>\n",[45,1591,1592],{"class":47,"line":60},[45,1593,724],{},[45,1595,1596],{"class":47,"line":66},[45,1597,1598],{},"        config.AddJsonFile(\"appsettings.json\");\n",[45,1600,1601],{"class":47,"line":72},[45,1602,1603],{},"        config.AddEnvironmentVariables();\n",[45,1605,1606],{"class":47,"line":78},[45,1607,1608],{},"        if (ctx.HostingEnvironment.IsDevelopment())\n",[45,1610,1611],{"class":47,"line":84},[45,1612,1613],{},"            config.AddUserSecrets\u003CProgram>();\n",[45,1615,1616],{"class":47,"line":91},[45,1617,1618],{},"    })\n",[45,1620,1621],{"class":47,"line":97},[45,1622,1623],{},"    .ConfigureServices((ctx, services) =>\n",[45,1625,1626],{"class":47,"line":102},[45,1627,724],{},[45,1629,1630],{"class":47,"line":108},[45,1631,1632],{},"        services.AddOptions\u003CWorkerOptions>()\n",[45,1634,1635],{"class":47,"line":114},[45,1636,1637],{},"            .BindConfiguration(\"Worker\")\n",[45,1639,1640],{"class":47,"line":119},[45,1641,1642],{},"            .ValidateDataAnnotations()\n",[45,1644,1645],{"class":47,"line":125},[45,1646,1647],{},"            .ValidateOnStart();\n",[45,1649,1650],{"class":47,"line":131},[45,1651,88],{"emptyLinePlaceholder":87},[45,1653,1654],{"class":47,"line":136},[45,1655,1656],{},"        services.AddHostedService\u003CMyWorker>();\n",[45,1658,1659],{"class":47,"line":142},[45,1660,1618],{},[45,1662,1663],{"class":47,"line":148},[45,1664,1665],{},"    .Build();\n",[45,1667,1668],{"class":47,"line":154},[45,1669,88],{"emptyLinePlaceholder":87},[45,1671,1672],{"class":47,"line":362},[45,1673,1674],{},"await host.RunAsync();\n",[45,1676,1677],{"class":47,"line":368},[45,1678,88],{"emptyLinePlaceholder":87},[45,1680,1681],{"class":47,"line":721},[45,1682,1683],{},"public class MyWorker : BackgroundService\n",[45,1685,1686],{"class":47,"line":727},[45,1687,176],{},[45,1689,1690],{"class":47,"line":733},[45,1691,1692],{},"    private readonly WorkerOptions _opts;\n",[45,1694,1695],{"class":47,"line":739},[45,1696,1697],{},"    public MyWorker(IOptions\u003CWorkerOptions> opts) => _opts = opts.Value;\n",[45,1699,1700],{"class":47,"line":745},[45,1701,88],{"emptyLinePlaceholder":87},[45,1703,1704],{"class":47,"line":751},[45,1705,1706],{},"    protected override async Task ExecuteAsync(CancellationToken ct)\n",[45,1708,1709],{"class":47,"line":756},[45,1710,724],{},[45,1712,1713],{"class":47,"line":762},[45,1714,1715],{},"        while (!ct.IsCancellationRequested)\n",[45,1717,1719],{"class":47,"line":1718},30,[45,1720,1721],{},"        {\n",[45,1723,1725],{"class":47,"line":1724},31,[45,1726,1727],{},"            DoWork();\n",[45,1729,1731],{"class":47,"line":1730},32,[45,1732,1733],{},"            await Task.Delay(TimeSpan.FromSeconds(_opts.PollIntervalSeconds), ct);\n",[45,1735,1737],{"class":47,"line":1736},33,[45,1738,1739],{},"        }\n",[45,1741,1743],{"class":47,"line":1742},34,[45,1744,748],{},[45,1746,1748],{"class":47,"line":1747},35,[45,1749,254],{},[10,1751,1753],{"id":1752},"recap","Recap",[15,1755,1756,1757,1759,1760,1762,1763,1765,1766,1769,1770,1772],{},"The configuration system layers providers — appsettings.json → environment-specific file →\nUser Secrets → environment variables → command-line — with later sources winning. Read config\nthrough ",[31,1758,797],{}," (startup value, never reloads), ",[31,1761,813],{}," (per-request),\nor ",[31,1764,829],{}," (live updates in singletons). Always add ",[31,1767,1768],{},".ValidateDataAnnotations() .ValidateOnStart()"," to critical options so bad config causes a clean startup failure rather\nthan a runtime surprise. Use User Secrets locally; use environment variables or a cloud\nsecrets manager in production. Use ",[31,1771,1506],{}," to enforce global constraints that\nthird-party libraries cannot override.",[1774,1775,1776],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":41,"searchDepth":54,"depth":54,"links":1778},[1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790],{"id":12,"depth":54,"text":13},{"id":20,"depth":54,"text":21},{"id":264,"depth":54,"text":265},{"id":402,"depth":54,"text":403},{"id":518,"depth":54,"text":519},{"id":840,"depth":54,"text":841},{"id":985,"depth":54,"text":986},{"id":1178,"depth":54,"text":1179},{"id":1407,"depth":54,"text":1408},{"id":1500,"depth":54,"text":1501},{"id":1570,"depth":54,"text":1571},{"id":1752,"depth":54,"text":1753},"How ASP.NET Core configuration actually works — layered providers, the IOptions family, strongly-typed settings, and validating config at startup before bad values cause runtime errors.","medium","md",".NET Core",{},"\u002Fblog\u002Fdotnet-aspnet-core-configuration","\u002Fdotnet\u002Faspnet-core\u002Fconfiguration",{"title":5,"description":1791},"blog\u002Fdotnet-aspnet-core-configuration","Configuration","ASP.NET Core","aspnet-core","2026-06-23","qm7yJqlLcBFx9ZKIVRtpK7TJhyQ8ATgQRG-gXuKoVpw",1782244086923]