Ploch.Common.Extensions.Configuration
Extension methods for binding configuration sections to strongly-typed options classes.
Overview
Ploch.Common.Extensions.Configuration provides two IServiceCollection extension methods that simplify the IOptions<T> / Configure<T> registration pattern from Microsoft.Extensions.Options. Instead of repeating the section name as a magic string at every call site, you either place a [ConfigurationSection] attribute on the options class or derive the section name from the class name automatically.
The package targets net8.0.
Installation
dotnet add package Ploch.Common.Extensions.Configuration
Key Types
| Type | Kind | Description |
|---|---|---|
ConfigurationSectionAttribute |
Attribute | Decorates an options class with an explicit configuration section name |
ConfigurationOptionsExtensions |
Static class | Extension methods AddConfigurationSection<T> and AddConfigurationOptions<TMain, TSub> on IServiceCollection |
Usage Examples
Binding a top-level section by class name
Without the attribute, the section name defaults to the class name. Given the following appsettings.json:
{
"SmtpSettings": {
"Host": "mail.example.com",
"Port": 587
}
}
Register and consume IOptions<SmtpSettings>:
public class SmtpSettings
{
public string Host { get; set; } = string.Empty;
public int Port { get; set; }
}
// Startup / Program.cs
services.AddConfigurationSection<SmtpSettings>(configuration);
// Consumer
public class EmailSender(IOptions<SmtpSettings> options) { ... }
Using [ConfigurationSection] to override the section name
When the class name does not match the JSON key, apply the attribute:
[ConfigurationSection("Smtp")]
public class SmtpSettings
{
public string Host { get; set; } = string.Empty;
public int Port { get; set; }
}
// Maps to the "Smtp" key rather than "SmtpSettings"
services.AddConfigurationSection<SmtpSettings>(configuration);
Binding a nested sub-section
AddConfigurationOptions binds a child property of a parent section using a strongly-typed lambda — no string literals for the sub-section path:
{
"Application": {
"Database": {
"ConnectionString": "Data Source=app.db"
}
}
}
public class ApplicationSettings
{
public DatabaseSettings Database { get; set; } = new();
}
public class DatabaseSettings
{
public string ConnectionString { get; set; } = string.Empty;
}
services.AddConfigurationOptions<ApplicationSettings, DatabaseSettings>(
configuration,
app => app.Database);
The section is resolved as Application:Database, and IOptions<DatabaseSettings> is registered in the container.
Method Signatures
// Top-level section binding
public static IServiceCollection AddConfigurationSection<TSection>(
this IServiceCollection services,
IConfiguration configuration)
where TSection : class, new();
// Nested sub-section binding
public static IServiceCollection AddConfigurationOptions<TMainSection, TSubSection>(
this IServiceCollection services,
IConfiguration configuration,
Expression<Func<TMainSection, TSubSection>> subSectionProperty)
where TSubSection : class;
Section Name Resolution
AddConfigurationSection<TSection> resolves the section name in this order:
- The
SectionNamefrom a[ConfigurationSection("...")]attribute onTSection, if present. - The class name of
TSectionas a fallback.
Related Libraries
- Ploch.Common.DependencyInjection — ServicesBundle pattern for organising DI registrations; can use
AddConfigurationSectioninside aConfigurableServicesBundle