Skip to main content
.NET

Validate Settings Using The IOptions Pattern in .NET

Christian Schou Køster

We have all been there 😅 configuration options or settings that we needed in our applications, but they were not available or in the wrong format. In this quick tutorial I am going to show you how easy it is to add validation to your options/settings classes using IOptions.

With the options-pattern, we can write strongly typed options/settings that our application can use at runtime. This makes our code more robust as we become type-safe.

The only problem I have encountered a lot of times with users of my code is that they write invalid values for their option fields. It tends to cause a lot of problems as the code tries to parse and use them, and that results in errors as they are incorrect.

So... I thought it would be nice to have a quick tutorial that showcases how easily we can get around that problem. It is actually fairly simple to solve that problem and make sure the users of your software are using it in the intended way.

🙋 What is a strongly typed configuration?

A strongly typed configuration is a class where we define each settings/options property inside. Let's say I wanted to integrate my app with the Marketstack API, I could create a MarketstackSettings class that would contain the required details.

public sealed class MarketstackSettings
{
  public string AccessKey { get; init; }
}

This of course has to be defined inside appsettings.json for the application, so that we can get the values for our configuration of the app.

"MarketstackSettings" {
  "AccessKey" : "YOUR_ACCESS_KEY"
}

If you want a key, you can get it by signing up at the link below.

Sign Up - marketstack
Sign up for a marketstack account and get instant access to market data for 125,000+ stock tickers, 72 global stock exchanges and 30 years of historical data.

Alright! Now we simply need to configure the MarketstackSettings at the startup of our app.

builder.Services.Configure<MarketstackSettings>(
  builder.Configuration.GetSection(nameof(MarketstackSettings)));

This makes it able for us to inject the configuration values for that settings class using IOptions<MarketstackSettings>().

public class MarketstackService : IMarketstackService
{
  private readonly MarketstackSettings _marketstackSettings;

  public class MarketstackService(IOptions<MarketstackSettings> marketstackSettings)
  {
    _marketstackSettings = marketstackSettings.Value;
  }

  public void GetMarketData()
  {
    var accessToken = _marketstackSettings.AccessKey;

    ...
  }
}

🎯 Why isn't that a bullet-proof solution?

Let's say we would ship our app with the configuration of our market settings like I did above. That would make the user of the app responsible for entering the correct information in the configuration of the app.

Many developers would have no problem doing this if they carefully read the documentation and understood what was expected by the app at startup. But from experience I just know that most engineers skip where they can (not because they are dumb, but because they are lazy and will deal with issues like this later). So... how can we solve that problem? 😄

We could use validation for the configuration properties ⚙️

🧑‍💻 Using validation with the options-pattern

Luckily for us, .NET provides an easy way of performing validation for the properties we have in the settings class. All we have to do is add data annotations on the properties.

Let's say we wanted to make the AccessKey required. All we have to do is add the [Required] attribute on the property.

public sealed class MarketstackSettings
{
  [Required]
  public string AccessKey { get; init; }
}

Now for the configuration part, we need to tell .NET that we want to validate the data annotations on the properties.

It's a minor change. Delete the configuration from before and update it to the following:

builder.Services
  .AddOptions<MarketstackSettings>()
  .BindConfiguration(nameof(MarketstackSettings))
  .ValidateDataAnnotations();

What did we just do now? 🤔

  • AddOptions<MarketstackSettings>() will return OptionsBuilder<MarketstackSettings>() that will bind to our MarketstackSettings class.
  • BindConfiguration(nameof(MarketstackSettings)) will make sure to bind the actual values from our configuration file appsettings.json.
  • ValidateDataAnnotations() will enable validation by using the added data annotations on the properties in our settings class. Here it will make sure there is a value in the AccessKey property as we specified that it was required.

Now if you try to inject the MarketstackSettings and the AccessKey is missing, we would encounter a runtime exception. Awesome! ✌️

🚀 Validate settings at startup

What if I told you that it is possible to validate your configuration properties at startup to avoid runtime exceptions later down the road? 😎

It is totally possible, and all you have to do is call ValidateOnStart() when you configure the settings.

builder.Services
  .AddOptions<MarketstackSettings>()
  .BindConfiguration(nameof(MarketstackSettings))
  .ValidateDataAnnotations()
  .ValidateOnStart();

Now our application will throw an exception when starting up if the AccessKey is missing during startup.

📝 Summary

In this short tutorial you learned how to use the options-pattern in .NET for retrieving configuration values from appsettings.json and bind them to your configuration classes.

You also learned how to validate the data annotations on the settings classes from the option values, and do it at the startup of your application. This should help you minimize the number of runtime errors or exceptions that could happen.

If you got any questions, please leave a comment below. Until next time - happy coding! ✌️

Christian Schou Køster