Cocoar.Configuration.AspNetCore 3.3.0

dotnet add package Cocoar.Configuration.AspNetCore --version 3.3.0
                    
NuGet\Install-Package Cocoar.Configuration.AspNetCore -Version 3.3.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="Cocoar.Configuration.AspNetCore" Version="3.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cocoar.Configuration.AspNetCore" Version="3.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Cocoar.Configuration.AspNetCore" />
                    
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 Cocoar.Configuration.AspNetCore --version 3.3.0
                    
#r "nuget: Cocoar.Configuration.AspNetCore, 3.3.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 Cocoar.Configuration.AspNetCore@3.3.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=Cocoar.Configuration.AspNetCore&version=3.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Cocoar.Configuration.AspNetCore&version=3.3.0
                    
Install as a Cake Tool

Reactive, strongly-typed configuration layering for .NET

Cocoar.Configuration

Elevates configuration from hidden infrastructure to an observable, safety‑enforced subsystem you can trust under change and failure.

License: Apache-2.0 NuGet Downloads

πŸ“– Articles & Deep Dives

β€’ Reactive, Strongly-Typed Configuration in .NET: Introducing Cocoar.Configuration v3.0 (Part 1)
Learn how v3.0 simplifies configuration management with zero-ceremony DI, atomic multi-config updates, and reactive patterns.

β€’ Config-Aware Rules in .NET β€” The Power Feature of Cocoar.Configuration (Part 2)
Dive deeper into atomic recompute, required vs optional rules, and config-aware conditional logic for dynamic, tenant-aware setups.


Shouldn't configuration be this easy?

builder.Services.AddCocoarConfiguration(rule => [
    rule.For<AppSettings>().FromFile("appsettings.json").Select("App"),
    rule.For<AppSettings>().FromEnvironment("APP_")
]);

It is. AppSettings is now injectable β€” and automatically updates when configs change.


Install

dotnet add package Cocoar.Configuration
dotnet add package Cocoar.Configuration.AspNetCore

# Optional providers:
dotnet add package Cocoar.Configuration.HttpPolling
dotnet add package Cocoar.Configuration.MicrosoftAdapter

Links: Cocoar.Configuration Β· AspNetCore Β· HttpPolling Β· MicrosoftAdapter


Why Cocoar.Configuration?

Microsoft's IConfiguration works, but configuration deserves better. Here's what you get:

  • Zero ceremony – Define a class, add a rule, inject it. No Configure<T>() calls, no IOptions<T> wrappers.
  • Atomic multi-config updates – IReactiveConfig<(T1, T2, T3)> means multiple configs stay in sync. Never see inconsistent state.
  • Config-aware rules – Rules can access earlier config to make decisions. Perfect for multi-tenant and dynamic scenarios.
  • Reactive by default – Subscribe to changes automatically. No manual IOptionsMonitor wiring.
  • Explicit layering – Rules execute in order, last write wins. No hidden merge logic.
  • Interface deserialization – Support for interface-typed properties in config classes with explicit mapping.
  • Built-in health monitoring – Track provider status and config changes with IConfigurationHealthService.

DI Lifetimes: Concrete config types are registered as Scoped (stable snapshot per request), while IReactiveConfig<T> is Singleton (continuous live updates). These defaults can be customized via the setup parameter.

Migration from IOptions

Before (IConfiguration + IOptions):

// Startup configuration
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("App"));

// Injection - requires wrapper
public class MyService(IOptions<AppSettings> options)
{
    var settings = options.Value; // Unwrap every time
}

After (Cocoar.Configuration):

// Startup configuration
builder.Services.AddCocoarConfiguration(rule => [
    rule.For<AppSettings>().FromFile("appsettings.json").Select("App")
]);

// Direct injection - no wrapper
public class MyService(AppSettings settings)
{
    // Just use it
}

// Or reactive
public class MyService(IReactiveConfig<AppSettings> config)
{
    config.Subscribe(newSettings => /* handle changes */);
}

Quick Example

var builder = WebApplication.CreateBuilder(args);

// Define your configuration rules
builder.Services.AddCocoarConfiguration(rule => [
    rule.For<AppSettings>().FromFile("appsettings.json").Select("App"),
    rule.For<AppSettings>().FromEnvironment("APP_"),
    rule.For<DatabaseConfig>().FromFile("appsettings.json").Select("Database")
]);

var app = builder.Build();

// Direct injection - just use your POCO
app.MapGet("/api/status", (AppSettings settings) => 
    new { settings.Version, settings.FeatureFlags });

app.Run();

For reactive scenarios (long-lived services):

public class CacheWarmer : BackgroundService
{
    private readonly IReactiveConfig<AppSettings> _config;
    
    public CacheWarmer(IReactiveConfig<AppSettings> config)
    {
        _config = config;
        
        // Subscribe once - rebuild cache when settings change
        _config.Subscribe(newSettings =>
        {
            Console.WriteLine($"Config changed, rebuilding cache...");
            RebuildCache(newSettings);
        });
    }
    
    protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.CompletedTask;
}

SignalR hub that streams atomic multi-config updates

public class ConfigHub : Hub
{
    private readonly IReactiveConfig<(AppSettings App, DatabaseConfig Db)> _configs;
    
    public ConfigHub(IReactiveConfig<(AppSettings App, DatabaseConfig Db)> configs)
    {
        _configs = configs;
        
        // Stream config changes to connected clients - always atomic
        _configs.Subscribe(async tuple =>
        {
            var (app, db) = tuple;
            await Clients.All.SendAsync("ConfigUpdated", new { app.Version, db.ConnectionString });
        });
    }
}

What You Can Do

Layer Configuration Sources

builder.Services.AddCocoarConfiguration(rule => [
    rule.For<AppSettings>().FromFile("appsettings.json"),           // Base
    rule.For<AppSettings>().FromFile("appsettings.Production.json"), // Environment
    rule.For<AppSettings>().FromEnvironment("APP_"),                 // Overrides
    rule.For<AppSettings>().FromCommandLine()                        // Final overrides (highest priority)
]);
// Rules execute in order - last write wins

Environment variable mapping:
Hierarchical keys use __ (double underscore):

APP_Database__Host=localhost
APP_Database__Port=5432
# Maps to: AppSettings.Database.Host and AppSettings.Database.Port

Command-line argument mapping:
Hierarchical keys use : or __:

dotnet run --Database:Host=localhost --Database:Port=5432 --Verbose
# Maps to: AppSettings.Database.Host, AppSettings.Database.Port, and AppSettings.Verbose (true)

Flexible switch prefixes for command-line arguments:
Use any prefix style (--, -, /, @, #, %) - even multiple at once:

// Single custom prefix
rule.For<AppConfig>().FromCommandLine(["-"])     // Unix-style
rule.For<AppConfig>().FromCommandLine(["/"])     // Windows-style
rule.For<AppConfig>().FromCommandLine(["@"])     // Custom semantic style

// Multiple prefixes simultaneously
rule.For<AppConfig>().FromCommandLine(["--", "-", "/"])
# Mix different styles in the same command line
dotnet run --host=localhost -port=8080 /verbose

# Or use semantic prefixes for self-documenting CLIs
invoke.exe @target=server #issue=123 %env=prod

Prefix filtering for command-line arguments:
Map arguments to specific configuration types:

builder.Services.AddCocoarConfiguration(rule => [
    rule.For<AppConfig>().FromCommandLine("app_"),
    rule.For<DatabaseConfig>().FromCommandLine("db_")
]);
dotnet run --app_host=localhost --db_connectionstring="Server=localhost"
# --app_host β†’ AppConfig.Host (prefix stripped)
# --db_connectionstring β†’ DatabaseConfig.ConnectionString (prefix stripped)

Required vs Optional Rules

// Required - fails fast if missing
rule.For<CoreSettings>().FromFile("required.json").Required()

// Optional - graceful degradation at runtime (default)
rule.For<OptionalSettings>().FromFile("optional.json")

Conditional Rules

rule.For<TenantSettings>().FromFile("tenant.json"),

rule.For<PremiumFeatures>().FromFile("premium.json")
    .When(accessor => accessor.GetRequiredConfig<TenantSettings>().IsPremium)
// Rules can access earlier config to make decisions

Dynamic Configuration

rule.For<TenantSettings>().FromFile("tenant.json"),

rule.For<ApiSettings>().FromHttpPolling(accessor =>
{
    var tenant = accessor.GetRequiredConfig<TenantSettings>();
    return new HttpPollingRuleOptions(
        $"https://{tenant.Region}.api.example.com/config",
        pollInterval: TimeSpan.FromMinutes(5)
    );
})
// Rules can derive behavior from earlier rules

Interface Exposure

builder.Services.AddCocoarConfiguration(rule => [
    rule.For<AppSettings>().FromFile("appsettings.json")
], setup => [
    setup.ConcreteType<AppSettings>().ExposeAs<IAppSettings>()
]);

// Both AppSettings and IAppSettings are injectable
public class MyService(IAppSettings settings) { }

Interface Deserialization

When your configuration classes have interface-typed properties, you need to map them to concrete types for JSON deserialization:

// Configuration with interface properties
public class AppSettings
{
    public string AppName { get; set; }
    public ILoggingConfig Logging { get; set; }  // Interface property!
}

builder.Services.AddCocoarConfiguration(rule => [
    rule.For<AppSettings>().FromEnvironment()  // or FromFile, FromHttpPolling, etc.
], setup => [
    // Map interface to concrete type for deserialization
    setup.Interface<ILoggingConfig>().DeserializeTo<LoggingConfig>()
]);

Why is this needed? When loading configuration from JSON sources (files, environment variables, HTTP), properties typed as interfaces cannot be deserialized directly. This mapping tells the deserializer which concrete type to instantiate.

Common scenarios:

  • Environment variables: Logging__LogLevel__Default=Debug
  • Visual Studio hot reload injecting logging configuration
  • Modular configuration with abstracted dependencies

Supports nested interfaces: If your interface properties contain other interface properties, just register all the mappings and they'll work at any depth.


Providers

  • File – JSON files with automatic change detection and reload
  • Environment Variables – Prefix-based with hierarchical mapping (__ for nesting)
  • Command-Line Arguments – POSIX-style parsing with prefix support and nested configuration
  • HTTP Polling – Remote config with caching and change detection (Cocoar.Configuration.HttpPolling)
  • Microsoft Adapter – Bridge existing IConfiguration sources (Cocoar.Configuration.MicrosoftAdapter)
  • Static/Observable – In-memory for testing and development

Examples

Explore real-world scenarios in the examples directory:

Example Description
BasicUsage ASP.NET Core with file + environment layering
FileLayering Multi-file layering (base/env/local)
DynamicDependencies Rules derived from earlier config
ConditionalRulesExample Config-aware conditional rules
TupleReactiveExample Atomic multi-config snapshots
HttpPollingExample Remote HTTP config polling
MicrosoftAdapterExample Integrate existing IConfiguration sources

View all examples β†’


Documentation

Topic Link
Getting Started Quickstart Guide
Architecture & How It Works Architecture
Provider Details Providers
Reactive Configuration Reactive Config
Health Monitoring Health Monitoring
Interface Binding Binding
Migration from v2.x Migration Guide
Advanced Scenarios Deep Dive

Testing

Over 200 automated tests covering:

  • Configuration layering and rule execution
  • Reactive updates and atomic snapshots
  • Provider reliability and failover
  • Concurrent access and race conditions
  • Health monitoring and status tracking

See the Testing Guide for details.


Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

  • Semantic Versioning – Breaking changes increment major version
  • Apache 2.0 License – See LICENSE and NOTICE
  • Trademarks – See TRADEMARKS.md

Security

  • Don't commit secrets to config files
  • Use environment variables or secure providers for sensitive data
  • Enable TLS/HTTPS for remote config endpoints
  • Consider Azure Key Vault or similar via the Microsoft Adapter

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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
3.3.0 175 10/23/2025
3.2.0 166 10/23/2025
3.1.1 166 10/19/2025
3.1.0 157 10/19/2025
3.0.0 172 10/12/2025
2.0.0 104 10/11/2025
1.1.0 173 9/25/2025
1.0.0 203 9/21/2025
0.15.0 291 9/18/2025
0.14.0 310 9/17/2025
0.13.0 315 9/17/2025
0.12.0 308 9/17/2025
0.11.1 297 9/16/2025
0.11.0 298 9/16/2025
0.10.0 302 9/16/2025
0.9.2 246 9/15/2025
0.9.1 221 9/14/2025
0.9.0 144 9/14/2025
0.4.0 158 9/13/2025