How to Use the Options Pattern in ASP.NET Core for Strongly Typed Configuration
Why You Need the Options Pattern for Configuration
When building applications in ASP.NET Core, configuration plays a critical role. Almost every project needs to load values from appsettings.json or environment variables, database connection strings, API keys, feature flags, and more.
The most common way to read configuration is by using the IConfiguration interface and accessing values with string keys like:
var value = configuration["MySection:MyValue"];
While this works, it comes with problems:
- Keys are just strings, so typos cause runtime errors.
- No compile-time safety or IntelliSense support.
- Hard to maintain as the number of settings grows.
The Options Pattern solves these issues by mapping configuration sections into strongly-typed classes. Instead of relying on raw strings, you work with C# objects that give you type safety, validation, and a cleaner design.
In this article, we’ll explore how to use the Options Pattern in ASP.NET Core with a Minimal API example.
The Options Pattern
The Options Pattern in ASP.NET Core uses classes to provide strongly typed access to groups of related settings. Instead of scattering configuration values throughout your code, you define dedicated classes that represent a logical group of settings.
When configuration settings are isolated into separate classes, the app follows two important software engineering principles:
- Encapsulation
Classes that depend on configuration settings only rely on the specific settings they need, not the entire configuration source. - Separation of Concerns
Settings for different areas of the application are independent and not coupled to each other. For example, database settings can live in one class, while email or API settings live in others.
Another advantage is that Options provide a mechanism to validate configuration data at startup. This prevents your app from running with invalid or missing settings.
For more details, see the official Microsoft documentation on Options validation.
How to Use the Options Pattern
Using the Options Pattern in ASP.NET Core follows a simple process:
- Create a class
Define a C# class that represents a group of related settings (for example ApiSettings with properties like BaseUrl,MaxRetries, andTimeoutSeconds). - Define configuration in appsettings.json
Add a section in appsettings.json that matches the class name or the chosen section name. - Bind the configuration to the class
InProgram.cs, register the class with the dependency injection container using Configure<T> and bind it to the corresponding section in the configuration. - Inject the settings where needed
Access the configuration values in your code by injecting IOptions<T>, IOptionsSnapshot<T>, or IOptionsMonitor<T>. Each of these interfaces provides a slightly different behavior for accessing configuration data.
This approach ensures that configuration data is strongly typed, validated, and easy to maintain across different parts of the application.
Example: Custom Middleware in ASP.NET Core Minimal API
Before we start with the code, it’s important to mention a design principle that also applies to middleware: the Single Responsibility Principle (SRP).
Each middleware should ideally take care of one responsibility only.
- If you try to combine multiple concerns (for example logging, correlation IDs, and exception handling) into a single middleware, the code becomes harder to test and maintain.
- By keeping them separate, each middleware is focused and reusable in different projects.
This is also the reason why the ASP.NET Core pipeline is so flexible: you can compose as many middleware as you want, each doing a specific job.
In the following example we’ll create two distinct middleware:
- One for logging slow requests.
- Another for adding correlation IDs.
Both are small, independent, and easy to understand — and when combined in the pipeline, they provide powerful observability features.
Example: Using the Options Pattern in ASP.NET Core Minimal API
Disclaimer: This example is purely for educational purposes. There are better ways to write code and applications that can optimize this example. Use this as a starting point for learning, but always strive to follow best practices and improve your implementation.
Prerequisites
Before starting, make sure you have the following installed:
- .NET SDK: Download and install the .NET SDK if you haven’t already.
- Visual Studio Code (VSCode): Install Visual Studio Code for a lightweight code editor.
- C# Extension for VSCode: Install the C# extension for VSCode to enable C# support.
Step 1 – Create a Minimal API Project
Open your terminal and run:
dotnet new web -n OptionsPatternDemo
cd OptionsPatternDemo
This will generate a minimal ASP.NET Core project with a basic Program.cs file.
Step 2 – Add Configuration to appsettings.json
Open appsettings.json and add a new section for ApiSettings:
{
"ApiSettings": {
"BaseUrl": "https://example.com/api",
"MaxRetries": 3,
"TimeoutSeconds": 30
}
}

Step 3 – Create the Settings Class
Add a new file ApiSettings.cs with the following content:
public class ApiSettings
{
public string BaseUrl { get; set; } = string.Empty;
public int MaxRetries { get; set; }
public int TimeoutSeconds { get; set; }
}
This class represents the configuration section and gives you strongly typed access to the settings.

Step 4 – Register the Options in Program.cs
In Program.cs, bind the configuration to the ApiSettings class:
var builder = WebApplication.CreateBuilder(args);
// Register ApiSettings in the DI container
builder.Services.Configure<ApiSettings>(
builder.Configuration.GetSection("ApiSettings"));
var app = builder.Build();
Step 5 – Use the Options in a Minimal API Endpoint
Now inject IOptions<ApiSettings> into an endpoint and return the values:
app.MapGet("/settings", (IOptions<ApiSettings> options) =>
{
var settings = options.Value;
return Results.Ok(new
{
settings.BaseUrl,
settings.MaxRetries,
settings.TimeoutSeconds
});
});
app.Run();

Run the project and open the Settings Api in your browser. You should see the settings returned as JSON.

Validating Options
One of the key benefits of the Options Pattern is the ability to validate configuration at startup. This prevents your application from running with invalid or missing values.
To enable validation, first install the Microsoft.Extensions.Options.DataAnnotations package:
dotnet add package Microsoft.Extensions.Options.DataAnnotations
Updating the Settings Class
Use data annotation attributes to enforce validation rules:
using System.ComponentModel.DataAnnotations;
public class ApiSettings
{
[Required]
[Url]
public string BaseUrl { get; set; } = string.Empty;
[Range(1, 10)]
public int MaxRetries { get; set; }
[Range(1, 120)]
public int TimeoutSeconds { get; set; }
}
Registering with Validation
In Program.cs, configure ApiSettings with validation:
builder.Services
.AddOptions<ApiSettings>()
.Bind(builder.Configuration.GetSection("ApiSettings"))
.ValidateDataAnnotations()
.ValidateOnStart();
What Happens Now?
- If
BaseUrlis missing or not a valid URL, the app fails to start. - If
MaxRetriesis outside the range 1–10, startup fails. - If
TimeoutSecondsis outside the range 1–120, startup fails.
This way, configuration errors are caught early instead of surfacing at runtime.

Putting it all together, your Program.cs should look like this:
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOptions<ApiSettings>()
.Bind(builder.Configuration.GetSection("ApiSettings"))
.ValidateDataAnnotations()
.ValidateOnStart();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapGet("/settings", (IOptions<ApiSettings> options) =>
{
var settings = options.Value;
return Results.Ok(new
{
settings.BaseUrl,
settings.MaxRetries,
settings.TimeoutSeconds
});
});
app.Run();
public class ApiSettings
{
[Required]
[Url]
public string BaseUrl { get; set; } = string.Empty;
[Range(1, 10)]
public int MaxRetries { get; set; }
[Range(1, 120)]
public int TimeoutSeconds { get; set; }
}
Conclusion
The Options Pattern is one of those features in ASP.NET Core that looks simple at first glance, but brings significant long-term benefits. By grouping related configuration into strongly typed classes, you make your applications more maintainable, testable, and less error-prone.
This pattern helps enforce important software engineering principles such as Encapsulation and Separation of Concerns, reducing the risk of having configuration code scattered across the project. With support for dependency injection, your services only depend on the settings they actually need.
Another key advantage is validation. Instead of letting your application run with missing or invalid configuration values, you can validate them at startup. This provides faster feedback during development and safer deployments in production.
And remember: the Options Pattern is not limited to web applications. You can apply the same approach in console apps, worker services, or any other .NET application where structured configuration is required.
Key takeaways:
- Strongly typed configuration, safer and easier to maintain compared to string-based access.
- Encapsulation & Separation of Concerns, each part of your app only depends on the settings it needs.
- Validation support, catch configuration errors early at startup instead of at runtime.
- Flexibility, works not only with ASP.NET Core APIs but also with console apps, background services, and other .NET applications.
Adopting the Options Pattern is a small change in the way you structure configuration, but it pays off with cleaner design and fewer runtime surprises.
If you think your friends or network would find this article useful, please consider sharing it with them. Your support is greatly appreciated.
Thanks for reading!
Discover CodeSwissKnife Bar, your all-in-one, offline Developer Tools from Your Menu Bar
