How to Use Options Pattern in C# for Typed Settings
The Options pattern in .NET provides a strongly typed approach to
managing application settings, replacing brittle string-based
configuration keys with structured C# objects. This article explains how
to define settings classes, bind them to configuration sources such as
appsettings.json, register them within the Dependency
Injection (DI) container, and consume them effectively across your
application using IOptions<T>,
IOptionsSnapshot<T>, and
IOptionsMonitor<T>.
1. Define the Settings Model
Create a Plain Old CLR Object (POCO) that mirrors the structure of your configuration section.
public class EmailSettings
{
public const string SectionName = "EmailSettings";
public string SmtpServer { get; set; } = string.Empty;
public int Port { get; set; }
public string SenderEmail { get; set; } = string.Empty;
public bool EnableSsl { get; set; }
}2. Define
Configuration in appsettings.json
Add the corresponding section to your appsettings.json
file. Ensure the property names match the properties in your C#
class.
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"EmailSettings": {
"SmtpServer": "smtp.example.com",
"Port": 587,
"SenderEmail": "noreply@example.com",
"EnableSsl": true
}
}3. Register the Options in the DI Container
In modern .NET applications (Program.cs), bind the
configuration section to your options class using the
Configure method.
var builder = WebApplication.CreateBuilder(args);
// Bind the configuration section to the EmailSettings class
builder.Services.Configure<EmailSettings>(
builder.Configuration.GetSection(EmailSettings.SectionName));
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();4. Inject and Use the Settings
Inject the options into your services or controllers via constructor
injection. The base interface is IOptions<T>.
using Microsoft.Extensions.Options;
public class EmailService
{
private readonly EmailSettings _settings;
public EmailService(IOptions<EmailSettings> options)
{
_settings = options.Value;
}
public void SendEmail(string recipient, string subject, string body)
{
var smtpHost = _settings.SmtpServer;
var port = _settings.Port;
// Logic to send email
}
}5. Choosing the Right Options Interface
The .NET runtime offers three interfaces for consuming options, depending on lifetime and dynamic reload requirements:
IOptions<T>:- Lifetime: Singleton.
- Behavior: Reads configuration once at application startup. It does not update if configuration changes occur while the app is running.
- Best for: Settings that never change without restarting the app.
IOptionsSnapshot<T>:- Lifetime: Scoped.
- Behavior: Recomputed once per HTTP request or scope. Reflects changes in external configuration files without restarting the app. Cannot be injected into Singleton services.
- Best for: Web applications needing fresh settings per request.
IOptionsMonitor<T>:- Lifetime: Singleton.
- Behavior: Provides real-time notifications when
configuration values change via the
CurrentValueproperty and anOnChangecallback. Can be safely injected into Singleton services. - Best for: Background services or Singletons that need real-time configuration reload.
Example Using
IOptionsSnapshot<T>
public class NotificationController : ControllerBase
{
private readonly EmailSettings _settings;
public NotificationController(IOptionsSnapshot<EmailSettings> options)
{
_settings = options.Value; // Updated if appsettings.json changes
}
}6. Adding Validation to Settings
To prevent the application from starting with invalid or missing settings, enable data validation using data annotations.
Update the model:
using System.ComponentModel.DataAnnotations;
public class EmailSettings
{
public const string SectionName = "EmailSettings";
[Required]
public string SmtpServer { get; set; } = string.Empty;
[Range(1, 65535)]
public int Port { get; set; }
[EmailAddress]
public string SenderEmail { get; set; } = string.Empty;
}Register validation in Program.cs:
builder.Services.AddOptions<EmailSettings>()
.Bind(builder.Configuration.GetSection(EmailSettings.SectionName))
.ValidateDataAnnotations()
.ValidateOnStart(); // Ensures validation occurs at app startup rather than first access