ThisCalculator

Everyday

How Net Options Configuration Works with DI

Updated · Standard formula, shown below

Average pace 5.00 min/km
Average speed
12.00 km/h
Result
Finish time
50 min
You entered
Distance
10 km
You entered

How Net Options Configuration Works with DI

Net options configuration in .NET uses the IOptions<T>, IOptionsSnapshot<T>, and IOptionsMonitor<T> interfaces to bind strongly-typed settings from configuration sources (like appsettings.json) and inject them into services. You register the options class with services.Configure<T>(configuration.GetSection(...)), and then depend on the appropriate IOptions interface in your classes. This article explains how the binding works, the differences between the three interfaces, and common pitfalls.

Background: Why Options Exist

Before the options pattern, developers often read configuration values directly via IConfiguration or used static settings. That scattered approach made testing hard and introduced magic strings. The options pattern centralizes settings into a POCO class, validates them at startup, and provides a clean way to inject them. It also supports reloading (with IOptionsMonitor) and scoped snapshots (IOptionsSnapshot).

How the Binding Works

The configuration system maps keys from your configuration source to properties on your options class. For example, given a class:

public class MyOptions { public string Name { get; set; } public int Count { get; set; } }

and appsettings.json containing:

{ "MyOptions": { "Name": "Test", "Count": 42 } }

you register it with:

services.Configure<MyOptions>(configuration.GetSection("MyOptions"));

The binder matches property names case-insensitively and converts strings to the target type. Nested objects and arrays are supported. You can also bind directly to an instance with configuration.Get<MyOptions>().

The Three IOptions Interfaces

Choose based on lifetime and reload needs:

  • IOptions<T>: Singleton, reads values once at registration. Good for static settings that never change.
  • IOptionsSnapshot<T>: Scoped, reads values once per request. Good for web apps where you want fresh values per request but not per access.
  • IOptionsMonitor<T>: Singleton, but tracks changes and raises events. Good for long-running services that must react to config changes (e.g., feature flags).

If you inject IOptions<T> in a singleton and the config changes, you won’t see the update. Use IOptionsMonitor for that.

How to Choose the Right Interface

Ask two questions:

  1. Does the setting change while the app runs? If no, use IOptions<T>.
  2. Do you need per-request freshness? If yes and you’re in a web app, use IOptionsSnapshot<T>.

If you’re building a background service (singleton), IOptionsMonitor<T> lets you read the current value anytime and subscribe to changes. For most simple apps, IOptions<T> is enough.

Common Problems and Misconceptions

  • Not calling GetSection: If you pass the whole configuration, the binder looks for keys at the root, not under your section.
  • Property type mismatch: The binder throws if a value can’t be converted (e.g., string “abcâ€� to int).
  • Forgetting to register: You must call services.Configure; otherwise you get a “No IOptions<T> registeredâ€� error.
  • Using IOptions in a singleton and expecting reload: It won’t reload; that’s by design.
  • Case sensitivity: The binder is case-insensitive, but the JSON keys must match property names ignoring case.

Comparison: IOptions vs. Manual Binding

Manual binding with configuration.Get<MyOptions>() gives you a plain object, but you lose the ability to inject it via DI and to get reload support. The options pattern also integrates with validation attributes (e.g., [Required]) if you call services.AddOptions<T>().Validate(...). For simple apps, manual binding can reduce boilerplate, but options are the standard for maintainable code.

Cost and Performance Considerations

Options are cheap. The binder runs once at startup for IOptions<T>, once per scope for IOptionsSnapshot<T>, and once per change for IOptionsMonitor<T>. The overhead is negligible compared to typical I/O. However, avoid putting large collections in options if they change frequently; consider a cache or a dedicated service instead.

Safety and Best Practices

Never store secrets (passwords, connection strings) in options that get logged. Use the Secret Manager in development and environment variables in production. Always validate options at startup using ValidateDataAnnotations() or a custom validator to fail fast. Also, avoid binding to a singleton service that captures IOptionsSnapshot—it will throw because the scope is already disposed.

Frequently Asked Questions

Why am I getting “IOptions<MyOptions> has not been registered�?

You forgot to call services.Configure<MyOptions>(...) in ConfigureServices. Add that line, and ensure the section name matches your config file.

How do I reload options when the config file changes?

Use IOptionsMonitor<T> and subscribe to OnChange. The default configuration provider reloads appsettings.json when the file changes, and the monitor picks it up.

Can I use options in a console app?

Yes, but you need to build a ServiceCollection and add the configuration providers manually. The options pattern works in any .NET app, not just ASP.NET Core.

What’s the difference between IOptionsSnapshot and IOptionsMonitor?

IOptionsSnapshot is scoped and gives you a fresh copy per request. IOptionsMonitor is singleton and gives you the current value at any time, plus change notifications. Choose snapshot for web requests, monitor for background services.

Can I bind options from environment variables?

Yes, add the environment variable provider and use a section name. For example, MyOptions__Name maps to MyOptions:Name in the binder.

Related

Formula used
in t | Finish time | min | 50 | 0
in d | Distance | km | 10 | 0
out pace | Average pace | min/km | t / d | 2
out speed | Average speed | km/h | d / (t / 60) | 2

Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.