microsoft-extensions-configuration
Configuration patterns using Microsoft.Extensions.Configuration. Covers configuration providers, binding, validation, and best practices for .NET applications. Use when setting up configuration in .NET applications, implementing configuration validation with IValidateOptions, or managing settings across different environments.
What this skill does
# Microsoft.Extensions Configuration Patterns
## When to Use This Skill
Use this skill when:
- Binding configuration from appsettings.json to strongly-typed classes
- Validating configuration at application startup (fail fast)
- Implementing complex validation logic for settings
- Designing configuration classes that are testable and maintainable
- Understanding IOptions<T>, IOptionsSnapshot<T>, and IOptionsMonitor<T>
## Why Configuration Validation Matters
**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, making debugging difficult.
**The Solution:** Validate configuration at startup. If configuration is invalid, the application fails immediately with a clear error message. This is the "fail fast" principle.
```csharp
// BAD: Fails at runtime when someone tries to use the service
public class EmailService
{
public EmailService(IOptions<SmtpSettings> options)
{
var settings = options.Value;
// Throws NullReferenceException 10 minutes into production
_client = new SmtpClient(settings.Host, settings.Port);
}
}
// GOOD: Fails at startup with clear error
// "SmtpSettings validation failed: Host is required"
```
---
## Pattern 1: Basic Options Binding
### Define a Settings Class
```csharp
public class SmtpSettings
{
public const string SectionName = "Smtp";
public string Host { get; set; } = string.Empty;
public int Port { get; set; } = 587;
public string? Username { get; set; }
public string? Password { get; set; }
public bool UseSsl { get; set; } = true;
}
```
### Bind from Configuration
```csharp
// In Program.cs or service registration
builder.Services.AddOptions<SmtpSettings>()
.BindConfiguration(SmtpSettings.SectionName);
// appsettings.json
{
"Smtp": {
"Host": "smtp.example.com",
"Port": 587,
"Username": "[email protected]",
"Password": "secret",
"UseSsl": true
}
}
```
### Consume in Services
```csharp
public class EmailService
{
private readonly SmtpSettings _settings;
// IOptions<T> - singleton, read once at startup
public EmailService(IOptions<SmtpSettings> options)
{
_settings = options.Value;
}
}
```
---
## Pattern 2: Data Annotations Validation
For simple validation rules, use Data Annotations:
```csharp
using System.ComponentModel.DataAnnotations;
public class SmtpSettings
{
public const string SectionName = "Smtp";
[Required(ErrorMessage = "SMTP host is required")]
public string Host { get; set; } = string.Empty;
[Range(1, 65535, ErrorMessage = "Port must be between 1 and 65535")]
public int Port { get; set; } = 587;
[EmailAddress(ErrorMessage = "Username must be a valid email address")]
public string? Username { get; set; }
public string? Password { get; set; }
public bool UseSsl { get; set; } = true;
}
```
### Enable Data Annotations Validation
```csharp
builder.Services.AddOptions<SmtpSettings>()
.BindConfiguration(SmtpSettings.SectionName)
.ValidateDataAnnotations() // Enable attribute-based validation
.ValidateOnStart(); // Validate immediately at startup
```
**Key Point:** `.ValidateOnStart()` is critical. Without it, validation only runs when the options are first accessed, which could be minutes or hours into application runtime.
---
## Pattern 3: IValidateOptions<T> for Complex Validation
Data Annotations work for simple rules, but complex validation requires `IValidateOptions<T>`:
### When to Use IValidateOptions
| Scenario | Data Annotations | IValidateOptions |
|----------|------------------|------------------|
| Required field | ✅ | ✅ |
| Range check | ✅ | ✅ |
| Regex pattern | ✅ | ✅ |
| Cross-property validation | ❌ | ✅ |
| Conditional validation | ❌ | ✅ |
| External service checks | ❌ | ✅ |
| Custom error messages with context | Limited | ✅ |
| Dependency injection in validator | ❌ | ✅ |
### Implementing IValidateOptions
```csharp
using Microsoft.Extensions.Options;
public class SmtpSettingsValidator : IValidateOptions<SmtpSettings>
{
public ValidateOptionsResult Validate(string? name, SmtpSettings options)
{
var failures = new List<string>();
// Required field validation
if (string.IsNullOrWhiteSpace(options.Host))
{
failures.Add("Host is required");
}
// Range validation
if (options.Port is < 1 or > 65535)
{
failures.Add($"Port {options.Port} is invalid. Must be between 1 and 65535");
}
// Cross-property validation
if (!string.IsNullOrEmpty(options.Username) && string.IsNullOrEmpty(options.Password))
{
failures.Add("Password is required when Username is specified");
}
// Conditional validation
if (options.UseSsl && options.Port == 25)
{
failures.Add("Port 25 is typically not used with SSL. Consider port 465 or 587");
}
// Return result
return failures.Count > 0
? ValidateOptionsResult.Fail(failures)
: ValidateOptionsResult.Success;
}
}
```
### Register the Validator
```csharp
builder.Services.AddOptions<SmtpSettings>()
.BindConfiguration(SmtpSettings.SectionName)
.ValidateDataAnnotations() // Run attribute validation first
.ValidateOnStart();
// Register the custom validator
builder.Services.AddSingleton<IValidateOptions<SmtpSettings>, SmtpSettingsValidator>();
```
**Order matters:** Data Annotations run first, then IValidateOptions validators. All failures are collected and reported together.
---
## Pattern 4: Validators with Dependencies
IValidateOptions validators are resolved from DI, so they can have dependencies:
```csharp
public class DatabaseSettingsValidator : IValidateOptions<DatabaseSettings>
{
private readonly ILogger<DatabaseSettingsValidator> _logger;
private readonly IHostEnvironment _environment;
public DatabaseSettingsValidator(
ILogger<DatabaseSettingsValidator> logger,
IHostEnvironment environment)
{
_logger = logger;
_environment = environment;
}
public ValidateOptionsResult Validate(string? name, DatabaseSettings options)
{
var failures = new List<string>();
if (string.IsNullOrWhiteSpace(options.ConnectionString))
{
failures.Add("ConnectionString is required");
}
// Environment-specific validation
if (_environment.IsProduction())
{
if (options.ConnectionString?.Contains("localhost") == true)
{
failures.Add("Production cannot use localhost database");
}
if (!options.ConnectionString?.Contains("Encrypt=True") == true)
{
_logger.LogWarning("Production database connection should use encryption");
}
}
// Validate connection string format
if (!string.IsNullOrEmpty(options.ConnectionString))
{
try
{
var builder = new SqlConnectionStringBuilder(options.ConnectionString);
if (string.IsNullOrEmpty(builder.DataSource))
{
failures.Add("ConnectionString must specify a Data Source");
}
}
catch (Exception ex)
{
failures.Add($"ConnectionString is malformed: {ex.Message}");
}
}
return failures.Count > 0
? ValidateOptionsResult.Fail(failures)
: ValidateOptionsResult.Success;
}
}
```
---
## Pattern 5: Named Options
When you have multiple instances of the same settings type (e.g., multiple database connections):
```csharp
// appsettings.json
{
"Databases": {
"Primary": {
"ConnectionString"Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.