# Using Options Pattern in .NET Core for Strongly Typed Configuration

Hello everyone! In this tutorial, we are going to learn about the Options Pattern in ASP.NET Core, understand its purpose, and explore how to use it effectively.

If you want to learn via video, here is the Youtube video of this blog post:

%[https://www.youtube.com/watch?v=MASk-qHgkpw] 

## Problem with Weakly-Typed Configuration

Let's start by looking at a simple example. I have a GET endpoint named `CityStatus`, which returns the name and population of a city. The data for name and population is being read from the appsettings file under the key `CityStatus`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1679944148525/f014ad91-4199-4cd3-84c1-acba04eec563.png align="center")

Here's how the data in the appsettings file looks like:

```json
{
  "CityStatus": {
    "Name": "Istanbul",
    "Population": 20000
  }
}
```

When I execute the endpoint, the response is the expected configuration data: `Istanbul` and `20000`.

However, I don't have strong typing to the configuration keys in the controller's constructor. This is where the Options Pattern comes in.

## Creating a Strongly Typed Class for Configuration

First, let's create a class for our `CityStatus` configuration. I'll name this class `CityStatusOptions` and define two properties: `Name` and `Population`.

```csharp
public class CityStatusOptions
{
    public string Name { get; set; }
    public int Population { get; set; }
}
```

Next, we need to bind our `CityStatusOptions` to the configuration. To do this, we'll use the `Configure` method in the `Startup` class.

```csharp
services.Configure<CityStatusOptions>(Configuration.GetSection("CityStatus"));
```

Now, we can inject this strongly typed configuration using the `IOptions<CityStatusOptions>` interface in the controller.

```csharp
public CityStatusController(IOptions<CityStatusOptions> options)
{
    _options = options.Value;
}
```

We can now access the data in the endpoint using the `_options.Name` and `_options.Population` properties.

![City Status endpoint with IOptions](https://cdn.hashnode.com/res/hashnode/image/upload/v1679944234416/05ea53f1-1941-49f1-a08c-6bad87f34b5e.png align="center")

Executing the endpoint again, I get the same response as before, but now with a strongly typed configuration.

## Handling Configuration Updates with IOptionsSnapshot and IOptionsMonitor

If we update the population value in the appsettings file and execute the endpoint again without restarting the application, we'll notice that the Options Pattern still returns the old value.

This is because the Options Pattern reads the data once and always returns the same value. To handle updated configuration values, we can use two interfaces: `IOptionsSnapshot<T>` and `IOptionsMonitor<T>`.

Here's how we can inject these interfaces and return the updated values in the endpoint:

```csharp
public CityStatusController(IOptions<CityStatusOptions> options,
    IOptionsSnapshot<CityStatusOptions> optionsSnapshot,
    IOptionsMonitor<CityStatusOptions> optionsMonitor)
{
    _options = options.Value;
    _optionsSnapshot = optionsSnapshot.Value;
    _optionsMonitor = optionsMonitor.CurrentValue;
}
```

With `IOptionsSnapshot<T>` and `IOptionsMonitor<T>`, we can now get the updated values in the response when the configuration is changed.

Although both interfaces provide updated values, there is a key difference between them: their lifecycles.

* `IOptionsMonitor<T>` is a singleton and always returns the updated value, but it is always injected as a singleton.
    
* `IOptionsSnapshot<T>` is scoped, and it reads the data from the configuration every time it is constructed.
    

![City Status endpoint with OptionsSnapshot and OptionsMonitor](https://cdn.hashnode.com/res/hashnode/image/upload/v1679944279628/c28b8dd7-2b48-49e5-b468-1c507cb64235.png align="center")

## Validating Configuration using Data Annotations

Another feature of the Options Pattern is validating configurations using data annotations. For example, to ensure the `Population` value is not less than zero, we can use the \[Range\] attribute:

```csharp
public class CityStatusOptions
{
    public string Name { get; set; }

    [Range(0, long.MaxValue)]
    public int Population { get; set; }
}
```

To apply data validation, we need to bind the Options Pattern differently using the `AddOptions` method in the `Startup` class.

```csharp
services.AddOptions<CityStatusOptions>()
    .Bind(Configuration.GetSection("CityStatus"))
    .ValidateDataAnnotations();
```

If we run the project and update the `Population` value to a negative number, an exception will be thrown when accessing the endpoint.

However, if we want the application to throw an exception during bootstrapping if the initial configuration is invalid, we need to use the `ValidateOnStart` method:

```csharp
services.AddOptions<CityStatusOptions>()
    .Bind(Configuration.GetSection("CityStatus"))
    .ValidateDataAnnotations()
    .ValidateOnStart();
```

Now, when running the project with an invalid initial configuration, the application throws an exception immediately.

## Conclusion

In this tutorial, we learned about the Options Pattern in ASP.NET Core and its benefits for strongly typed configuration. We covered how to create a strongly typed class for configuration and how to handle configuration updates using `IOptionsSnapshot<T>` and `IOptionsMonitor<T>`, and how to validate configuration using data annotations.

Thank you for reading, and I hope this tutorial was helpful. Stay tuned for the next content; may the force be with you!
