$npx -y skills add Aaronontheweb/dotnet-skills --skill microsoft-extensions-configurationMicrosoft.Extensions.Options patterns including IValidateOptions, strongly-typed settings, validation on startup, and the Options pattern for clean configuration management.
| 1 | # Microsoft.Extensions Configuration Patterns |
| 2 | |
| 3 | ## When to Use This Skill |
| 4 | |
| 5 | Use this skill when: |
| 6 | - Binding configuration from appsettings.json to strongly-typed classes |
| 7 | - Validating configuration at application startup (fail fast) |
| 8 | - Implementing complex validation logic for settings |
| 9 | - Designing configuration classes that are testable and maintainable |
| 10 | - Understanding IOptions<T>, IOptionsSnapshot<T>, and IOptionsMonitor<T> |
| 11 | |
| 12 | ## Reference Files |
| 13 | |
| 14 | - [advanced-patterns.md](advanced-patterns.md): Validators with dependencies, named options, complete production example (AkkaSettings), and testing validators |
| 15 | |
| 16 | ## Why Configuration Validation Matters |
| 17 | |
| 18 | **The Problem:** Applications often fail at runtime due to misconfiguration - missing connection strings, invalid URLs, out-of-range values. These failures happen deep in business logic, far from where configuration is loaded. |
| 19 | |
| 20 | **The Solution:** Validate configuration at startup. If invalid, fail immediately with a clear error message. |
| 21 | |
| 22 | ```csharp |
| 23 | // BAD: Fails at runtime when someone tries to use the service |
| 24 | public class EmailService |
| 25 | { |
| 26 | public EmailService(IOptions<SmtpSettings> options) |
| 27 | { |
| 28 | var settings = options.Value; |
| 29 | // Throws NullReferenceException 10 minutes into production |
| 30 | _client = new SmtpClient(settings.Host, settings.Port); |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | // GOOD: Fails at startup with clear error |
| 35 | // "SmtpSettings validation failed: Host is required" |
| 36 | ``` |
| 37 | |
| 38 | --- |
| 39 | |
| 40 | ## Pattern 1: Basic Options Binding |
| 41 | |
| 42 | ### Define a Settings Class |
| 43 | |
| 44 | ```csharp |
| 45 | public class SmtpSettings |
| 46 | { |
| 47 | public const string SectionName = "Smtp"; |
| 48 | |
| 49 | public string Host { get; set; } = string.Empty; |
| 50 | public int Port { get; set; } = 587; |
| 51 | public string? Username { get; set; } |
| 52 | public string? Password { get; set; } |
| 53 | public bool UseSsl { get; set; } = true; |
| 54 | } |
| 55 | ``` |
| 56 | |
| 57 | ### Bind from Configuration |
| 58 | |
| 59 | ```csharp |
| 60 | builder.Services.AddOptions<SmtpSettings>() |
| 61 | .BindConfiguration(SmtpSettings.SectionName); |
| 62 | |
| 63 | // appsettings.json |
| 64 | { |
| 65 | "Smtp": { |
| 66 | "Host": "smtp.example.com", |
| 67 | "Port": 587, |
| 68 | "Username": "user@example.com", |
| 69 | "Password": "secret", |
| 70 | "UseSsl": true |
| 71 | } |
| 72 | } |
| 73 | ``` |
| 74 | |
| 75 | ### Consume in Services |
| 76 | |
| 77 | ```csharp |
| 78 | public class EmailService |
| 79 | { |
| 80 | private readonly SmtpSettings _settings; |
| 81 | |
| 82 | // IOptions<T> - singleton, read once at startup |
| 83 | public EmailService(IOptions<SmtpSettings> options) |
| 84 | { |
| 85 | _settings = options.Value; |
| 86 | } |
| 87 | } |
| 88 | ``` |
| 89 | |
| 90 | --- |
| 91 | |
| 92 | ## Pattern 2: Data Annotations Validation |
| 93 | |
| 94 | For simple validation rules, use Data Annotations: |
| 95 | |
| 96 | ```csharp |
| 97 | using System.ComponentModel.DataAnnotations; |
| 98 | |
| 99 | public class SmtpSettings |
| 100 | { |
| 101 | public const string SectionName = "Smtp"; |
| 102 | |
| 103 | [Required(ErrorMessage = "SMTP host is required")] |
| 104 | public string Host { get; set; } = string.Empty; |
| 105 | |
| 106 | [Range(1, 65535, ErrorMessage = "Port must be between 1 and 65535")] |
| 107 | public int Port { get; set; } = 587; |
| 108 | |
| 109 | [EmailAddress(ErrorMessage = "Username must be a valid email address")] |
| 110 | public string? Username { get; set; } |
| 111 | |
| 112 | public string? Password { get; set; } |
| 113 | public bool UseSsl { get; set; } = true; |
| 114 | } |
| 115 | ``` |
| 116 | |
| 117 | ### Enable Data Annotations Validation |
| 118 | |
| 119 | ```csharp |
| 120 | builder.Services.AddOptions<SmtpSettings>() |
| 121 | .BindConfiguration(SmtpSettings.SectionName) |
| 122 | .ValidateDataAnnotations() // Enable attribute-based validation |
| 123 | .ValidateOnStart(); // Validate immediately at startup |
| 124 | ``` |
| 125 | |
| 126 | **Key Point:** `.ValidateOnStart()` is critical. Without it, validation only runs when the options are first accessed. |
| 127 | |
| 128 | --- |
| 129 | |
| 130 | ## Pattern 3: IValidateOptions<T> for Complex Validation |
| 131 | |
| 132 | Data Annotations work for simple rules, but complex validation requires `IValidateOptions<T>`: |
| 133 | |
| 134 | | Scenario | Data Annotations | IValidateOptions | |
| 135 | |----------|------------------|------------------| |
| 136 | | Required field | Yes | Yes | |
| 137 | | Range check | Yes | Yes | |
| 138 | | Cross-property validation | No | Yes | |
| 139 | | Conditional validation | No | Yes | |
| 140 | | External service checks | No | Yes | |
| 141 | | Dependency injection in validator | No | Yes | |
| 142 | |
| 143 | ### Implementing IValidateOptions |
| 144 | |
| 145 | ```csharp |
| 146 | using Microsoft.Extensions.Options; |
| 147 | |
| 148 | public class SmtpSettingsValidator : IValidateOptions<SmtpSettings> |
| 149 | { |
| 150 | public ValidateOptionsResult Validate(string? name, SmtpSettings options) |
| 151 | { |
| 152 | var failures = new List<string>(); |
| 153 | |
| 154 | if (string.IsNullOrWhiteSpace(options.Host)) |
| 155 | failures.Add("Host is required"); |
| 156 | |
| 157 | if (options.Port is < 1 or > 65535) |
| 158 | failures.Add($"Port {options.Port} is invalid. Must be between 1 and 65535"); |
| 159 | |
| 160 | // Cross-property validation |
| 161 | if (!string.IsNullOrEmpty(options.Username) && string.IsNullOrEmpty(options.Password)) |
| 162 | failures.Add("Password is required when Username is specified"); |
| 163 | |
| 164 | // Conditional validation |
| 165 | if (options.UseSsl && |