Skip to content

Config Merge

Apq.Cfg supports merging multiple configuration sources with level-based priority.

How It Works

Configuration sources are merged by their level value. Higher levels override lower levels.

Each configuration source has a default level. If not specified, the default level is used:

Source TypeDefault Level
Json, Ini, Xml, Yaml, Toml0
Redis, Database100
Consul, Etcd, Nacos, Apollo, Zookeeper200
Vault300
.env, EnvironmentVariables400
Level 400: Environment Variables  ─┐
Level 300: Vault                   │
Level 200: Consul                  ├─► Merged Configuration
Level 50:  config.local.json       │
Level 0:   config.json            ─┘

Example

Configuration Files

config.json (Level 0):

json
{
    "App": {
        "Name": "MyApp",
        "Port": 8080,
        "Debug": false
    }
}

config.local.json (Level 50):

json
{
    "App": {
        "Debug": true
    }
}

Code

csharp
var cfg = new CfgBuilder()
    .AddJsonFile("config.json")                              // Uses default level 0
    .AddJsonFile("config.local.json", level: 50, optional: true)
    .Build();

// Results:
// App:Name = "MyApp"      (from level 0)
// App:Port = 8080         (from level 0)
// App:Debug = true        (from level 50, overrides level 0)

Merge Rules

  1. Higher level wins: Values from higher levels override lower levels
  2. Key-by-key merge: Only matching keys are overridden
  3. Null handling: Setting a value to null removes it from the merged result
  4. Array handling: Arrays are replaced entirely, not merged

Write Target Selection

When writing values, you can specify the target level:

csharp
// Write to default (highest writable level)
cfg["App:Name"] = "NewName";

// Write to specific level
cfg.SetValue("App:Name", "NewName", targetLevel: 0);

Best Practices

Level RangePurposeExample
0Base configurationconfig.json
10-50Environment-specificconfig.Production.json
50-99Local overridesconfig.local.json
100Remote storageRedis, Database
200Config centersConsul, Nacos, Apollo
300Secret managementVault
400Environment variablesHighest priority overrides

Level intervals of 100 allow flexibility for custom levels in between.

Typical Scenarios

Environment-Specific Configuration

csharp
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";

var cfg = new CfgBuilder()
    .AddJsonFile("config.json")                                    // Uses default level 0
    .AddJsonFile($"config.{environment}.json", level: 10, optional: true)
    .AddEnvironmentVariables(prefix: "APP_")                   // Uses default level 400
    .Build();

Local Development Configuration

csharp
var cfg = new CfgBuilder()
    .AddJsonFile("config.json")                                                           // Uses default level 0
    .AddJsonFile("config.Development.json", level: 10, optional: true)
    .AddJsonFile("config.local.json", level: 50, writeable: true, optional: true, isPrimaryWriter: true)  // gitignore
    .Build();

With Remote Config Center

csharp
var cfg = new CfgBuilder()
    // Base configuration (uses default level 0)
    .AddJsonFile("config.json")
    .AddJsonFile($"config.{env}.json", level: 10, optional: true)

    // Local override
    .AddJsonFile("config.local.json", level: 50, writeable: true, optional: true, isPrimaryWriter: true)

    // Remote configuration (uses default level 200)
    .AddConsul(options => { ... })

    // Environment variables (uses default level 400)
    .AddEnvironmentVariables(prefix: "APP_")

    .Build();

Next Steps

Released under the MIT License