Zaiets.ConfigLoader 1.0.0

dotnet add package Zaiets.ConfigLoader --version 1.0.0
                    
NuGet\Install-Package Zaiets.ConfigLoader -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Zaiets.ConfigLoader" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Zaiets.ConfigLoader" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Zaiets.ConfigLoader" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Zaiets.ConfigLoader --version 1.0.0
                    
#r "nuget: Zaiets.ConfigLoader, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Zaiets.ConfigLoader@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Zaiets.ConfigLoader&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Zaiets.ConfigLoader&version=1.0.0
                    
Install as a Cake Tool

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.

NuGet License: MIT


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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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