Binding ASP.NET configuration to an interface
Tags: csharp, dotnet, oop, config
Published: 2022-06-25 13:09:20

With ASP.NET core you can bind configurations values from a variety of configuration providers be it JSON, memory configuration provider [](https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration) or your could write you own custom provider. The framework allows you bind configuration from these sources to concrete instances of a class with properties set the configuration values using the `IOptions<T>` method.

The `IOptions<T>` allows you register the instantiated configuration class.

A brief recap would be to load upload the config details say for example we are getting these details from the `appsettings.json`
For .NET 6 the `appsettings.json` is loaded up automatically.
For this example the `appsettings.json` would look like this
``` json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "CustomConfigValue": {
    "CustomProperty": "value here"
  }
}
```
In order to bind the `CustomConfigValue` node to class/record instance we would need to create a class/record
```c#
public class CustomConfigValue
{
    public string CustomProperty { get; set; }
}
```
then to bind JSON node `CustomConfigValue` we would do this in the `Program.cs`
```c#
builder.Services.Configure<CustomConfigValue>(builder.Configuration.GetSection(nameof(CustomConfigValue)));
```
Then in the class/controller constructor we set the value from `IOptions<T>` to the a class property of the same type
```c#
private readonly CustomConfigValue _customConfig;

public CustomConfigClass(IOptions<CustomConfigValue> customConfigValue)
{
    _customConfig = customConfigValue.Value;
}
``` 
There are a few benefits to using this method, for one it gives us the ease when mocking out the configuration details in our tests, another benefit is configuration reloading when config values change in the JSON file [](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-2.2).

**Another approach** In some case the use of `IOptions<T>` isn't beneficial and there are a lot of posts about how to bind directly to object instances instead. From our previous example what we would be doing different is that we would be doing this in the `Program.cs`
```c#
CustomConfigValue customConfig = new ();

builder.Configuration.Bind(nameof(CustomConfigValue), customConfig);

builder.Services.AddSingleton(typeof(CustomConfigValue), customConfig);
```
In the class/controller constructor we would be able to access the `CustomConfigValue` like this
```c#
private readonly CustomConfigValue _customConfig;

public CustomConfigClass(CustomConfigValue customConfigValue)
{
    _customConfig = customConfigValue;
}
```
This can be abstracted away from the `Program.cs` by creating a service extension then doing the work like this
```c#
public static class ConfigurationExtensions 
{
    public static void AddCustomConfigValue<T>(this IServiceCollection services, IConfigurationSection configSection) where T : class
    {
        T setting = configSection.Get<T>();
        services.TryAddSingleton<T>(setting);
    }
}
```
In the `Program.cs` we do this to bind the configuration to the class object
```c#
builder.Services.AddCustomConfigValue<CustomConfigValue>(builder.Configuration.GetSection(nameof(CustomConfigValue)));
```
This is all well and good, and covers most use cases, but one thing I find that this method has is yes, you guess it, doesn't bind to an interface. I really love the idea of abstractions, I think it's a concept everyone should apply in every part of the life, but I digress.
In order to bind to an interface let's create another method in the extension class
```c#
public static void AddCustomConfigValue<InterfaceT, T>(this IServiceCollection services, IConfigurationSection configSection)
    where T : InterfaceT
    where InterfaceT : class
{
    InterfaceT setting = configSection.Get<T>();
    services.TryAddSingleton(setting);
}
```
We would add an interface
```
public interface ICustomConfig
{
    string CustomProperty { get;}
}
```
Then have the CustomConfigValue implement the interface
```
public class CustomConfigValue : ICustomConfig
```
In the `Program.cs` we would do this to bind the configuration to the object
```c#
builder.Services.AddCustomConfigValue<ICustomConfig, CustomConfigValue>(builder.Configuration.GetSection(nameof(CustomConfigValue)));
```
And in our constructor we would be able to access the configuration with
```c#
private readonly ICustomConfig _customConfigViaInterface;

public CustomConfigClass(ICustomConfig customConfigViaInterface)
{
    _customConfigViaInterface= customConfigViaInterface;
}
```
You could do this to maintain some level of abstraction and aid testing via the binding of the object to an interface and having only `get` access via the interface.

Another thing to consider is to use the `IOptions<T>` but bind to a record. The idea behind this is to create an immutable configuration object, once instantiated properties can't be reassigned and enforced at compile time.