What is the Options validation pattern, and why validate configuration on startup?
Quick Answer
Bind config to an options class and validate it: builder.Services.AddOptions<SmtpOptions>().Bind(config.GetSection("Smtp")).ValidateDataAnnotations().ValidateOnStart();. ValidateOnStart() makes a missing or invalid setting fail immediately when the app boots, rather than at 2 a.m. the first time that feature runs.
It's binding a config section to a strongly typed options class and then validating it. I chain ValidateDataAnnotations for the attribute rules, or .Validate with a lambda for custom logic, and then ValidateOnStart. That last call is the important one — without it, validation only happens the first time something resolves the options, so a bad connection string or a missing URL doesn't blow up until some feature runs it, often in production. With ValidateOnStart the app refuses to boot, which is exactly what you want.
- Add
[Required],[Range],[Url]to the options class, or use.Validate(o => ..., "message")for custom rules, or implementIValidateOptions<T>for complex/cross-field validation. - Without
ValidateOnStart, validation runs lazily on first resolve — the error surfaces deep in a request, hard to attribute to config. - Fail-fast config validation is part of a good deployment safety net (alongside health checks and smoke tests).