Introduction

Configurations are usually stored as key-value pairs in configuration sources. Reading configuration data from Configuration sources in. NET is done using Configuration providers that load configuration data into our application, and the application reads the right configurations based on the runtime context and environment.

Configuration Service

ASP.NET Core comes with a built-in service IConfiguration that provides a single representation of all the configuration sources and it can be used to access any configuration value from multiple providers.

Using the IConfiguration service

1. Create a new .NET 6 web API with the name Config.Demo.WebApi.

var builder = WebApplication.CreateBuilder(args);

the preceding code snippet in
the Program.cs file provides the default configuration for the application in the following order:

MemoryConfigurationProvider
ChainedConfigurationProvider
JsonConfigurationProvider : loads the configurations from the appsettings.json file
JsonConfigurationProvider: loads the configurations from the appsettings.Environment.json file
EnvironmentVariablesConfigurationProvider

2. Add configuration data to the appsettings.json file

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ApiConfig": {
    "BaseAddress": "https://www.codifysimply.com/",
    "UserAgent": "Chrome",
    "TimeoutInSeconds": "5"
  }
}

3. Reading configuration data in the Program.cs file that we added in the previous section.

The builder.Configuration instance that you access in Program.cs implements the Microsoft.Extensions.Configuration.IConfiguration type.

    public interface IConfiguration
    {
        // Returns:
        // The configuration value.
        string this[string key] { get; set; }
        // Returns:
        // The configuration sub-sections.
        IEnumerable<IConfigurationSection> GetChildren();
        // Returns:
        //The Microsoft.Extensions.Configuration.IConfigurationSection.
        IConfigurationSection GetSection(string key);
    }

You can use the methods provided in the IConfiguartion type  to obtain the configuration data.

The following code uses a delimiter in the configuration keys to read the hierarchical configuration:

var baseAddress = builder.Configuration["ApiConfig:BaseAddress"];
var userAgent = builder.Configuration["ApiConfig:UserAgent"];
var timeoutInSeconds = builder.Configuration["ApiConfig:TimeoutInSeconds"];

Using a delimiter in configuration keys is error-prone and difficult to read and maintain, so the preferred approach to reading related configuration data is to use the Options Pattern which we will discuss briefly in the next section.

Using a delimiter in configuration keys is error-prone and difficult to read and maintain, so the preferred approach to reading related configuration data is to use the Options Pattern which we will discuss briefly in the next section.

Options Pattern In .NET 6.0

The options pattern uses classes to provide strongly-typed access to groups of related settings.

When configuration settings are isolated by scenarios into strongly typed classes, the application adheres to two important design principles :

The interface segregation principle (ISP), or encapsulation principle: Using interfaces that depend on the configuration settings you want to read insulates the different parts of an application from other parts
Separation of concerns: Settings for different parts of the application are separated from each other .

Also read https://dotnetcoder.com/client-credentials-flow-in-entra-id/

Options interfaces

Create the following ApiConfig class to bind it to the ApiConfig section in the appsettings.json

namespace Config.Demo.WebApi.Config
{
    public class ApiConfig
    {
        public string BaseAddress { get; set; } = string.Empty;
        public string UserAgent { get; set; } = string.Empty;
        public int TimeoutInSeconds { get; set; }
    }
}
IOptions interface​

IOptions<TOptions> is a singleton and therefore cannot read configuration data changes and can be injected into any service lifetime.

1. Create a new project folder named Services and a subfolder named Interfaces
2. Add an interface named ITransientService

using Config.Demo.WebApi.Config;
namespace Config.Demo.WebApi.Services.Interfaces
{
    public interface ITransientService
    {
        ApiConfig GetApiConfig();
    }
}

3. Add a class called TransientService that implements the above interface.

namespace Config.Demo.WebApi.Services
{
    public class TransientService : ITransientService
    {
        private readonly IOptions<ApiConfig> options;
        public TransientService(IOptions<ApiConfig> options)
        {
            this.options = options;
        }
        public ApiConfig GetApiConfig() => options.Value;
    }
}

IOptionsSnapshot interface

IOptionsSnapshot<TOptions> is scoped and therefore cannot be injected into a Singleton service and it can read the updated data at each injection resolution.

1. Create an interface named IScopedService

namespace Config.Demo.WebApi.Services.Interfaces
{
    public interface IScopedService
    {
        ApiConfig GetApiConfig();
    }
}

2. Add a class named ScopedService to the Services folder that implements the above interface.

namespace Config.Demo.WebApi.Services
{
    public class ScopedService : IScopedService
    {
        private readonly IOptionsSnapshot<ApiConfig> optionsSnapshot;
        public ScopedService(IOptionsSnapshot<ApiConfig> optionsSnapshot)
        {
            this.optionsSnapshot = optionsSnapshot;
        }
        public ApiConfig GetApiConfig() => optionsSnapshot.Value;
    }
}
IOptionsMonitor interface

IOptionsMonitor<TOptions> is a singleton service that retrieves current configurations at any time and is, therefore usable in singleton dependencies.

1. Create an interface named ISingletonService in the Interfaces folder

namespace Config.Demo.WebApi.Services.Interfaces
{
    public interface ISingletonService
    {
        ApiConfig GetApiConfig();
    }
}

2. Add a class named SingletonService to the Services folder that implements the above interface.

namespace Config.Demo.WebApi.Services
{
    public class SingletonService:ISingletonService
    {
        private readonly IOptionsMonitor<ApiConfig> optionsMonitor;
        public SingletonService(IOptionsMonitor<ApiConfig> optionsMonitor)
        {
            this.optionsMonitor = optionsMonitor;
        }
        public ApiConfig GetApiConfig() => optionsMonitor.CurrentValue;
    }
}

Consuming the services

1. Add the following code to the Program.cs file to bind the ApiConfig class to the ApiConfig section

var configuration = builder.Configuration;
builder.Services.Configure<ApiConfig>(configuration.GetSection(nameof(ApiConfig)));

2. To register the services in the DI container in  Program.cs, use the extension methods of IServiceCollection as shown in the following snippet:

builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();

3. Create a controller named ConfigController to consume the services

namespace Config.Demo.WebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ConfigController : ControllerBase
    {
        private readonly ITransientService transientService;
        private readonly IScopedService scopedService;
        private readonly ISingletonService singletonService;
        public ConfigController(ITransientService transientService,
            IScopedService scopedService,
            ISingletonService singletonService)
        {
            this.transientService = transientService;
            this.scopedService = scopedService;
            this.singletonService = singletonService;
        }
        [HttpGet]
        [Route("apiconfig")]
        public Dictionary<string, ApiConfig> GetConfig()
        {
            return new Dictionary<string, ApiConfig>
            {
                ["IOptions"] = transientService.GetApiConfig(),
                ["IOptionsSnapshot"] = scopedService.GetApiConfig(),
                ["IOptionsMonitor"] = singletonService.GetApiConfig()
            };
        }
    }
}

4. Run the application and hit the controller action

options pattern in asp.net core

5. Observe the output.

{
    "IOptions": {
        "baseAddress": "https://www.codifysimply.com/",
        "userAgent": "Chrome",
        "timeoutInSeconds": 5
    },
    "IOptionsSnapshot": {
        "baseAddress": "https://www.codifysimply.com/",
        "userAgent": "Chrome",
        "timeoutInSeconds": 5
    },
    "IOptionsMonitor": {
        "baseAddress": "https://www.codifysimply.com/",
        "userAgent": "Chrome",
        "timeoutInSeconds": 5
    }
}

5. While our app is running change the value of timeoutInSeconds in the appsettings.json from 5 to 30 and observe the output.

{
    "IOptions": {
        "baseAddress": "https://www.codifysimply.com/",
        "userAgent": "Chrome",
        "timeoutInSeconds": 5
    },
    "IOptionsSnapshot": {
        "baseAddress": "https://www.codifysimply.com/",
        "userAgent": "Chrome",
        "timeoutInSeconds": 30
    },
    "IOptionsMonitor": {
        "baseAddress": "https://www.codifysimply.com/",
        "userAgent": "Chrome",
        "timeoutInSeconds": 30
    }
}

IOptions<TOptions>cannot read configuration data changes, unlike IOptionsSnapshot<TOptions> and IOptionsMonitor<TOptions>.

Conclusion

In this post, we have explained the configuration service in .Net and focused on the Options pattern, which uses classes to provide strongly typed access to groups of related settings.

In the next post, we will take a look at the Azure Key Vault configuration provider

The code for the demo can be found  Here

Also read https://dotnetcoder.com/entity-framework-code-first-approach-in-net-8/

Enjoy This Blog?

Buy Me a Coffee Donate via PayPal

Discover more from Dot Net Coder

Subscribe to get the latest posts sent to your email.

Author

Ads Blocker Image Powered by Code Help Pro

Ads Blocker Detected!!!

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

Powered By
100% Free SEO Tools - Tool Kits PRO