Zaiets.ConfigLoader
1.0.0
dotnet add package Zaiets.ConfigLoader --version 1.0.0
NuGet\Install-Package Zaiets.ConfigLoader -Version 1.0.0
<PackageReference Include="Zaiets.ConfigLoader" Version="1.0.0" />
<PackageVersion Include="Zaiets.ConfigLoader" Version="1.0.0" />
<PackageReference Include="Zaiets.ConfigLoader" />
paket add Zaiets.ConfigLoader --version 1.0.0
#r "nuget: Zaiets.ConfigLoader, 1.0.0"
#:package Zaiets.ConfigLoader@1.0.0
#addin nuget:?package=Zaiets.ConfigLoader&version=1.0.0
#tool nuget:?package=Zaiets.ConfigLoader&version=1.0.0
Zaiets.ConfigLoader
Hierarchical config loader for .NET — merge JSON, YAML, and environment variables in priority order, bind to strongly-typed sections, validate with DataAnnotations, and hot-reload on file changes.
Installation
dotnet add package Zaiets.ConfigLoader
Quick Start
using Zaiets.ConfigLoader;
var config = await ConfigLoaderBuilder.Create()
.AddJsonFile("appsettings.json")
.AddYamlFile("appsettings.yaml", optional: true)
.AddEnvironmentVariables(prefix: "APP_")
.WithHotReload()
.BuildAsync();
// Raw string access
string? host = config.Get("Database:Host");
// Typed access with fallback
int port = config.Get<int>("Database:Port", defaultValue: 5432);
// Required key — throws if absent
string connStr = config.GetRequired("Database:ConnectionString");
Sources and Priority
Sources are merged in ascending priority order — higher priority wins on key collision.
| Method | Default Priority |
|---|---|
AddJsonFile(...) |
100, 200, 300… (auto-incremented) |
AddYamlFile(...) |
next auto-incremented slot |
AddEnvironmentVariables(...) |
next auto-incremented slot |
AddDictionary(...) |
next auto-incremented slot |
Pass an explicit priority: to override:
var config = ConfigLoaderBuilder.Create()
.AddJsonFile("base.json", priority: 100)
.AddJsonFile("production.json", priority: 200, optional: true)
.AddEnvironmentVariables("APP_", priority: 300)
.Build();
JSON Source
Standard JSON with nested objects and arrays:
{
"Database": {
"Host": "localhost",
"Port": 5432,
"Name": "mydb"
},
"AllowedHosts": ["api.example.com", "admin.example.com"]
}
Keys: Database:Host, Database:Port, AllowedHosts:0, AllowedHosts:1
YAML Source
database:
host: localhost
port: 5432
name: mydb
features:
dark_mode: true
max_connections: 100
Keys use the same colon-separated format as JSON.
Environment Variables
Double underscore __ acts as the section separator:
export APP__Database__Host=prod-db.internal
export APP__Database__Port=5432
With .AddEnvironmentVariables(prefix: "APP_") these become Database:Host and Database:Port.
Typed Sections
Bind a section to a POCO with GetSection<T>():
public class DatabaseConfig
{
public string Host { get; set; } = "localhost";
public int Port { get; set; } = 5432;
public string Name { get; set; } = string.Empty;
}
var db = config.GetSection<DatabaseConfig>("Database");
Console.WriteLine($"Connecting to {db.Host}:{db.Port}/{db.Name}");
Supported types: primitives, string, bool, decimal, DateTime, DateTimeOffset,
TimeSpan, Guid, Uri, enums, nullable variants, List<T>, and arrays.
Nested objects are bound recursively:
public class AppConfig
{
public DatabaseConfig Database { get; set; } = new();
public CacheConfig Cache { get; set; } = new();
}
var app = config.GetSection<AppConfig>(""); // root section
Validation
Decorate your config POCO with DataAnnotations and call GetAndValidateSection<T>():
using System.ComponentModel.DataAnnotations;
public class SmtpConfig
{
[Required]
public string Host { get; set; } = string.Empty;
[Range(1, 65535)]
public int Port { get; set; } = 587;
[Required, EmailAddress]
public string FromAddress { get; set; } = string.Empty;
}
// Throws ConfigValidationException with all errors listed if invalid
var smtp = config.GetAndValidateSection<SmtpConfig>("Smtp");
You can also validate manually:
using Zaiets.ConfigLoader.Validation;
var errors = ConfigValidator.Validate(myConfig);
if (errors.Length > 0)
{
foreach (var error in errors)
Console.Error.WriteLine(error.ErrorMessage);
}
Hot Reload
Enable .WithHotReload() on the builder. The loader re-merges all sources after a 500ms
debounce window when any watched file changes.
var config = await ConfigLoaderBuilder.Create()
.AddJsonFile("appsettings.json")
.AddYamlFile("appsettings.yaml", optional: true)
.WithHotReload(debounce: TimeSpan.FromSeconds(1))
.BuildAsync();
// Subscribe to reload notifications
config.Reloaded += (_, e) =>
Console.WriteLine($"Config reloaded from {e.FilePath} at {e.Timestamp}");
config.ReloadFailed += (_, e) =>
Console.Error.WriteLine($"Reload failed: {e.Exception.Message}");
// Manual reload is also available
await config.ReloadAsync();
Microsoft DI Integration
using Zaiets.ConfigLoader.Extensions;
// Register the loader
builder.Services.AddConfigLoader(b => b
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables("APP_")
.WithHotReload());
// Bind and register a typed section
builder.Services.AddConfigSection<DatabaseConfig>("Database", validate: true);
// Live section (re-reads on each resolution — useful with hot reload)
builder.Services.AddLiveConfigSection<FeatureFlags>("Features");
Inject as usual:
public class MyService(IConfigLoader config, DatabaseConfig db)
{
public void Run()
{
var timeout = config.Get<int>("Http:TimeoutMs", 5000);
Console.WriteLine($"DB: {db.Host}, timeout: {timeout}ms");
}
}
In-Memory Source (Testing)
Override specific values without touching files:
var config = ConfigLoaderBuilder.Create()
.AddJsonFile("appsettings.json")
.AddDictionary(new Dictionary<string, string?>
{
["Database:Host"] = "test-db",
["Database:Port"] = "5433",
["FeatureFlags:NewUI"] = "true"
}, name: "TestOverrides", priority: 9999)
.Build();
API Reference
ConfigLoaderBuilder
| Method | Description |
|---|---|
AddJsonFile(path, optional, priority) |
Add a JSON file source |
AddYamlFile(path, optional, priority) |
Add a YAML file source |
AddEnvironmentVariables(prefix, priority) |
Add environment variables |
AddDictionary(values, name, priority) |
Add an in-memory dictionary |
AddSource(IConfigSource) |
Add a custom source |
WithHotReload(debounce?) |
Enable file-watch hot reload |
Build() / BuildAsync() |
Build the IConfigLoader |
IConfigLoader
| Member | Description |
|---|---|
Get(key) |
Raw string value or null |
Get<T>(key, default?) |
Typed value with optional default |
GetRequired(key) |
String value, throws if missing |
GetSection(key) |
Flat sub-dictionary |
GetSection<T>(key) |
Bound typed POCO |
GetAndValidateSection<T>(key) |
Bound + DataAnnotations validated |
All |
All merged key-value pairs |
ReloadAsync() |
Manual reload trigger |
Reloaded |
Event fired on successful reload |
ReloadFailed |
Event fired on reload error |
License
MIT — see LICENSE.
Author: Vladyslav Zaiets — sarmkadan.com
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- YamlDotNet (>= 16.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 94 | 5/3/2026 |