# Adding Validation To The Options Pattern In ASP.NET Core

> In this week's newsletter I will show you how to add validation to the strongly typed configuration objects injected with IOptions. You have no guarantee those values were correctly read from the application settings, so let's make sure they are.

Published: 2023-01-07. Author: Milan Jovanović.

Canonical: https://milanjovanovic.tech/blog/adding-validation-to-the-options-pattern-in-asp-net-core

You add validation to the Options pattern with data annotation attributes like `[Required]` on the settings class, registered with `AddOptions`, `BindConfiguration`, and `ValidateDataAnnotations`.
Add `ValidateOnStart` and the validation runs when the application starts, instead of the first time you inject `IOptions`.

In this week's newsletter I will show you how to easily add **validation**
to the strongly typed configuration objects injected with `IOptions`.

The [**Options pattern**](https://milanjovanovic.tech/blog/how-to-use-the-options-pattern-in-asp-net-core-7) allows us to use classes to provide strongly typed
configuration values in our application at runtime.

But you have no guarantee that the configuration values injected with
`IOptions` will be correctly read from the application settings.

Let's see how we can introduce validation for `IOptions`, and make sure the application settings are correct.

## Strongly Typed Configuration

I first want to define a simple class that will represent our strongly
typed configuration. Let's say we want to integrate with the **GitHub API**,
so we create a `GitHubSettings` class to hold our configuration:

```csharp
public class GitHubSettings
{
    public string AccessToken { get; init; }

    public string RepositoryName { get; init; }
}
```

Inside of our `appsettings.json` file we need to create a section to
hold our configuration values:

```json
"GitHubSettings": {
    "AccessToken": "access-token-value",
    "RepositoryName": "youtube-projects"
}
```

And with this in place, we can configure our `GitHubSettings`:

```csharp
builder.Services.Configure<GitHubSettings>(
    builder.Configuration.GetSection("GitHubSettings"));
```

Finally, our `GitHubSettings` is properly configured and we can inject
it with `IOptions<GitHubSettings>`.

## What Could Go Wrong?

If we leave the implementation like this, we're moving the responsibility
for providing the correct configuration values to the developer. I'm not
saying we are the problem, but I've forgotten to add application settings
a few times. I'm sure this happened to you also.

Here are just a few things that can go wrong:

- Passing an incorrect section name to `IConfiguration.GetSection`
- Forgetting to add the settings values in `appsettings.json`
- Typo in a property name in the class or in the configuration
- Unbindale properties without a setter
- Data type mismatch resulting in incompatible values

Depending on which one of these mistakes is made, the application will
behave differently at runtime.

The best case scenario is that the incorrect application settings cause
a runtime exception, and you realize you have a problem and fix it.

The worst case scenario, and this happens more often than you may think,
is that the application silently fails. The application settings aren't
correctly set on the value provided by `IOptions`, but you don't get a
runtime exception. The problem may go undetected for some time.

How do we solve this?

## Validation For The Options Pattern

There is a simple way to introduce **validation** to the settings class
using **data annotations**. We just add the validation attributes that
we need to the properties of the settings class.

For example, we can add the `Required` attribute to the `GitHubSettings`
properties:

```csharp
public class GitHubSettings
{
    [Required]
    public string AccessToken { get; init; }

    [Required]
    public string RepositoryName { get; init; }
}
```

We have to slightly change how we configure the `GitHubSettings`:

```csharp
builder.Services
    .AddOptions<GitHubSettings>()
    .BindConfiguration("GitHubSettings")
    .ValidateDataAnnotations();
```

A few things to note here:

- `AddOptions` - returns an `OptionsBuilder<TOptions>` that binds to
  the `GitHubSettings` class
- `BindConfiguration` - binds the values from the configuration section
- `ValidateDataAnnotations` - enables **validation** using **data annotations**

With this in place, if we try to inject `GitHubSettings` with any of
the properties missing a value, we will get a runtime exception.

You can also define a **custom delegate** for the **validation** logic, instead
of using data annotations:

```csharp
builder.Services
    .AddOptions<GitHubSettings>()
    .BindConfiguration("GitHubSettings")
    .Validate(gitHubSettings =>
    {
        if (string.IsNullOrEmpty(gitHubSettings.AccessToken))
        {
            return false;
        }

        return true;
    });
```

## Running Validation At Application Start

It would be great if we could run **validation** on the configuration values
when our application is starting, instead of at runtime.

We can do that by calling `ValidateOnStart` method when configuring
our settings class:

```csharp
builder.Services
    .AddOptions<GitHubSettings>()
    .BindConfiguration("GitHubSettings")
    .ValidateDataAnnotations()
    .ValidateOnStart(); // 👈 the magic happens here
```

When we start the application, the validation will run on `GitHubSettings`
and an exception is thrown if validation fails. The validation exception
will look something like this:

```yaml
Unhandled exception. Microsoft.Extensions.Options.OptionsValidationException:
  DataAnnotation validation failed for 'GitHubSettings' members:
```

This shortens the feedback loop, and you will know right away that you have
a problem. This is much better than finding out that you have a problem at
runtime, like in the previous examples.

## Closing Thoughts

The **Options pattern** is very flexible and allows us to use strongly typed
settings in ASP.NET Core.

If you want to see how to implement the [**Options pattern**](https://youtu.be/wxYt0motww0),
I made a [**video about it where I go into the details.**](https://youtu.be/wxYt0motww0)
I covered the differences between `IOptions`, `IOptionsSnapshot` and `IOptionsMonitor`.

And now you know how to use the `ValidateOnStart` method, which was introduced
in **.NET 6**, to validate your application settings on app start up. This allows
you to learn about configuration issues as soon as possible, instead of at runtime.

I also made a video showing how to add [validation to the Option pattern](https://youtu.be/qRruEdjNVNE).

---

## Frequently asked questions

### How do you add validation to the options pattern in ASP.NET Core?

Add data annotation attributes such as Required to the settings class, then configure it with AddOptions, BindConfiguration, and ValidateDataAnnotations. If the bound values fail validation, injecting the options throws a runtime exception instead of silently using wrong configuration.

### What can go wrong when binding configuration to IOptions?

Passing an incorrect section name to GetSection, forgetting to add the values in appsettings.json, a typo in a property name, unbindable properties without a setter, or a data type mismatch. The worst case is the application silently failing with unset values, which can go undetected for some time.

### What does ValidateOnStart do in ASP.NET Core?

ValidateOnStart, introduced in .NET 6, runs validation on the bound settings when the application starts and throws an OptionsValidationException if validation fails. This shortens the feedback loop, so you learn about configuration issues right away instead of at runtime.

### Can you validate options without data annotations?

Yes. Instead of ValidateDataAnnotations, call Validate on the options builder with a custom delegate containing your validation logic, for example returning false when a required value like an access token is null or empty.

### Why should you validate strongly typed configuration?

Because you have no guarantee the values injected with IOptions were read correctly from the application settings. The best case is a runtime exception that reveals the problem. The worst case, which happens more often than you may think, is the application silently failing with incorrect settings.
