Kunal Pattni

Software, Data & Cloud Expert

Home Blog Clean code and reliable deployments using IOptions

Clean code and reliable deployments using IOptions

11/06/2025

The worst thing that can happen is to deploy bad code and not know it or have to wait until it's live to fix it. The Options pattern will take you one step closer to being able to blind deploy your applications and help you to encapsulate your code.

If you're a cloud engineer or developer trying to up your game, you'll want to see this.

You can have a look at the entire working sample at Sample on Github.

Bad Example

Let's start with a common pattern that I see and want to pull my hair out, injecting IConfiguration and using strings to extract your value:

public class BadExampleController(IConfiguration configuration) : Controller
{
    [HttpGet]
    public IActionResult Get()
    {
        return Ok(configuration["Sample:Name"]);
    }
} 

It looks okay until your application gets larger and you want to refactor something. Good luck checking every line of configuration in a 1000 line appsettings to make sure that you aren't removing something important or trying to debug why your application keeps failing when its just a spelling mistake or you're referring to an appsetting that no longer exists.

Most importantly, on deployment, this will successfully build and deploy and you will only know there is a problem when somebody tries to hit this endpoint and gets a HTTP 500: Internal Server Error

So what can we do about it?

Instead of rolling our own solution, Microsoft have handily created a simple but powerful extension called Options that does everything we need and more.

Setup a Class

Start by creating a class that represents the appsettings that you are interested in. If you are adding Google Maps to your application then this class might be called GoogleMapsSettings and contain your API key, zoom level and initial latitude and longitude. In this example, I've kept it simple and just used a string: Name and an integer: Age.

public class SampleSettings
{
    public const string SettingsName = "Sample";
    public required string Name { get; init; }
    public int Age { get; init; }
}

Looks pretty simple so far but worth noting: there's a static (const) property called SettingsName containing the name of the section in appsettings that contains Name and Age. The snippet below might help to understand the heirarchy:

{
  "Sample": {
    "Name": "MyName",
    "Age": 30
  }
}

It's also worth using init instead of set since we never want the settings to be mutable.

Register Options

Here's where the magic happens. We will register this as an Option on startup and perform validation at the same time:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddSampleOptions(this IServiceCollection services, IConfiguration configuration)
    {
        services
        .AddOptions<SampleSettings>()
            .Bind(configuration.GetRequiredSection(SampleSettings.SettingsName))
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddTransient <ISampleService, SampleService>();

        return services;
    }
}

So line by line:

Create an extension to the IServiceCollection which allows you to perform your dependency injection neatly in its own file to keep your Program.cs clean.

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddSampleOptions(this IServiceCollection services, IConfiguration configuration)
    {

Register SampleSettings as options and bind the options to a section of our appsettings using the SampleSettings.SettingsName that we created earlier (which is why it is const, so it can be statically)

services.AddOptions<SampleSettings>()
  .Bind(configuration.GetRequiredSection(SampleSettings.SettingsName))

Perform validation using data annotations. I won't go into the detail of this now but it allows you to add attributes to your properties like Regex or minimum and maximum sizes and have your application fail if those requirements are not met. Finally, perform the validation on startup, so your app intentionally fails early (during deployment and not after).

.ValidateDataAnnotations()
.ValidateOnStart();

The last two lines aren't relevant just yet but will make sense soon.

Create a Service

Next up, we create a service to use the Options. In our case it's probably a bit overkill but if you want to do something fancy with your settings before you use them all over the place this is probably the best place to do it.

public class SampleService : ISampleService
{
    private readonly SampleSettings _sampleSettings;

    public SampleService(IOptions<SampleSettings> sampleOptions)
    {
        _sampleSettings = sampleOptions.Value;
    }

There are other ways to do this but I prefer using a constructor for legibility. So you inject the IOptions and when you want to use it you get the .Value which will return the SampleSettings itself, populated with the appsettings you bound it to. I like to store the SampleSettings itself rather than the IOptions for use within the class hence the private readonly SampleSettings _sampleSettings;

The other things I do here are make sure that there is an interface ISampleSettings and create some example methods to show SampleSettings being used.

By the way, this is where the last two lines of our ServiceCollectionsExtension come in handy, to register our SampleService too before returning the IServiceCollection so calls can be chained in Program.cs.

services.AddTransient();

return services;

Quick note on folder structure

I prefer the following folder structure because it keeps all of your related files together in the same namespace because they are used together. It also allows you to have multiple ServiceCollectionExtensions scattered around your application because they have different namespaces and in my opinion it's just the perfect name for the file.

├── Services
│   └── Sample
│       ├── SampleService.cs
│       ├── SampleSettings.cs
│       └── ServiceCollectionExtensions.cs

Using Options in Razor Pages

Make sure you have your @using, your @inject and you use .Value and its pretty selef-explanatory after that.

@using Microsoft.Extensions.Options
@inject IOptions<SampleSettings> SampleOptions

@{
    ViewData["Title"] = "Home page";
    var sampleSettings = SampleOptions.Value;
}

@sampleSettings.Age

I personally wouldn't use the SampleSettings directly, I would use the SampleService but for the sake of example I've injected the IOptions instead of creating a cshtml.cs file where I would inject the SampleService to build my PageModel.

If that all went over your head, check out this page Microsoft Razor Docs to learn more about PageModel.

Using Options in Controllers

Similar usage, but here we use the ISampleService so no need to .Value.

public class SampleController(ISampleService sampleService) : Controller
{
    [HttpGet]
    public IActionResult Get()
    {
        return Ok(sampleService.GetName());
    }
}

Trying it out

You can check out the full example here Sample on Github

If you try to run it, visit http://localhost:5003/ for the Razor Page example and http://localhost:5003/sample for the Controller example.

Final thoughts

Use Options, they make your code better in too many ways and its so simple to use that even if you don't understand the need yet, you will once your application gets big enough.

Also, check out Microsoft's own docs on the topic which go into more detail on other areas of Options Microsft Options Docs