Table of Contents
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

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?
Discover more from Dot Net Coder
Subscribe to get the latest posts sent to your email.