Skip to content

Repository files navigation

ConfigBinder

NuGetLicense: MIT

ConfigBinder is a zero-reflection, highly performant configuration binding library for .NET, powered by Roslyn Source Generators. It allows you to bind strongly typed configuration classes directly from IConfiguration at compile time, eliminating the startup overhead and reflection cost associated with the built-in Microsoft.Extensions.Configuration binder.

Features

  • Zero Reflection: Uses Roslyn Source Generators to generate binding code at compile time.
  • AOT / Trimming Friendly: Completely avoids reflection, making it perfect for Native AOT and heavily trimmed applications.
  • Direct Access or IOptions: Choose between injecting your raw configuration objects directly (AddSingleton<MyConfig>) or using the standard IOptions<MyConfig> pattern.
  • Immediate.Validations Integration: Out-of-the-box support for validating configurations on startup using Immediate.Validations.
  • Custom Converters: Define property-level or global custom type converters for complex types.
  • Built-in Parsing: Automatically parses all standard primitives, enums, and any type implementing IParsable<T>.

Installation

Add the ConfigBinder package to your project. Since this is a source generator, you may want to reference it accordingly (though it provides attributes as well).

<PackageReferenceInclude="ConfigBinder"Version="x.y.z" />

Quick Start

1. Define your Configuration Model

Decorate your configuration class, struct, or record with [ConfigSection("SectionName")]. By default, properties must have an accessible setter (or init).

usingConfigBinder.Attributes;[ConfigSection("MyConfig")]publicsealedclassMyConfig{publicrequiredstringName{get;init;}publicrequiredintMaxRetries{get;init;}}

2. Register Generated Configurations

In your Program.cs or startup code, call the generated extension method RegisterGeneratedConfigs on your IServiceCollection.

varbuilder=WebApplication.CreateBuilder(args);// This single call registers all types decorated with [ConfigSection]builder.Services.RegisterGeneratedConfigs(builder.Configuration);varapp=builder.Build();// You can now resolve your config!varconfig=app.Services.GetRequiredService<IOptions<MyConfig>>();

Registration Modes

ConfigBinder supports two modes for registering your configuration objects:

  • RegistrationMode.DirectAccess: Registers the object directly as a singleton (services.AddSingleton<T>).
  • RegistrationMode.Options (Default): Registers the object using the standard Options pattern (services.AddOptions<T>()).

You can override the mode on a per-class basis:

[ConfigSection("MyConfig",Mode=ConfigRegistrationMode.DirectAccess)]publicclassMyOptionsConfig{/* ... */}

Or set an assembly-wide default:

[assembly:ConfigSectionDefaults(Mode=ConfigRegistrationMode.DirectAccess)]

Custom Converters

If you need to parse complex types that don't implement IParsable<T>, you can write custom converters. A converter is simply a type with a static method (default name Convert) taking a string and a string (property name) and returning the parsed type.

Property-Level Converter

[ConfigSection("Feature")]publicclassFeatureConfig{[ConfigConverter(typeof(MyCustomParser),"ParseMyType")]publicMyTypeSomeProperty{get;set;}}

Global Converter

Register a converter for a specific type across your entire assembly:

[assembly:ConfigTypeConverter(typeof(MyType),typeof(MyCustomParser))]

Validation

If your project references Immediate.Validations and your configuration type implements IValidationTarget<T>, ConfigBinder will automatically wire up IValidateOptions<T> when using RegistrationMode.Options. This ensures your configuration is strictly validated on application startup.

Warning

Validation works only in RegistrationMode.Options mode.

usingImmediate.Validations.Shared;[Validate][ConfigSection("ValidatedConfig")]publicsealedpartialclassValidatedConfig:IValidationTarget<ValidatedConfig>{publicrequiredstringHost{get;init;}publicrequiredintPort{get;init;}}

Example

A config model like

[Validate][ConfigSection("Validated")]internalsealedpartialclassValidatedConfig:IValidationTarget<ValidatedConfig>{[MinLength(10)]publicrequiredstringName{get;init;}publicrequiredfloatWeight{get;init;}publicrequiredDateTimeBuildDate{get;init;}}

will generate the following binding code:

internalstaticclassValidatedConfigConfigBinder{publicstaticValidatedConfigBind(IConfigurationconfiguration){varsection=configuration.GetSection("Validated");varinstance=newValidatedConfig{Name=ValidateString(section["Name"],"Name"),Weight=ParseFloat(section["Weight"],"Weight"),BuildDate=ParseIParsable<DateTime>(section["BuildDate"],"BuildDate"),};returninstance;}privatestaticstringValidateString(string?value,stringpropertyName){if(string.IsNullOrEmpty(value)){throwRequired(propertyName);}returnvalue;}privatestaticTParseIParsable<T>(string?value,stringpropertyName)whereT:IParsable<T>{if(string.IsNullOrEmpty(value)){throwRequired(propertyName);}if(T.TryParse(value,CultureInfo.InvariantCulture,outvart)){returnt;}throwBadValue(propertyName,value,typeof(T).Name);}privatestaticfloatParseFloat(string?value,stringpropertyName){if(string.IsNullOrEmpty(value)){throwRequired(propertyName);}if(float.TryParse(value,NumberStyles.Float,CultureInfo.InvariantCulture,outvarn)){returnn;}throwBadValue(propertyName,value,"float");}privatestaticInvalidOperationExceptionRequired(stringkey)=>new($"Required configuration key '{key}' is missing or empty");privatestaticInvalidOperationExceptionBadValue(stringkey,string?value,stringtype)=>new($"Configuration key '{key}' value '{value}' cannot be parsed as '{type}'");}

and the following extension method:

publicstaticclassGeneratedConfigRegistration{publicstaticIServiceCollectionRegisterGeneratedConfigs(thisIServiceCollectionservices,IConfigurationconfiguration){services.AddSingleton<IOptionsFactory<ValidatedConfig>>(sp =>newConfigBinderOptionsFactory<ValidatedConfig>(sp.GetRequiredService<IEnumerable<IConfigureOptions<ValidatedConfig>>>(),sp.GetRequiredService<IEnumerable<IPostConfigureOptions<ValidatedConfig>>>(),sp.GetRequiredService<IEnumerable<IValidateOptions<ValidatedConfig>>>(),
_ =>ValidatedConfigConfigBinder.Bind(configuration)));services.AddSingleton<IValidateOptions<ValidatedConfig>,ImmediateValidationOptionsValidator<ValidatedConfig>>();services.AddOptions<ValidatedConfig>().ValidateOnStart();returnservices;}}

About

Zero-reflection configuration binding library for .NET, powered by Roslyn Source Generators. It allows you to bind strongly typed configuration classes directly from `IConfiguration` at compile time, eliminating the startup overhead and reflection cost

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages