1

In my application I am using appsettings to load my configuration. I have the appsettings.json:

{
  "FileReader": {
    "InputDirectory": "tmp"
  }
}

I would like to have the option to override the value of InputDirectory with a command line argument shorthand switch (e.g. -i). I know I can override it via an environment variable named FileReader__InputDirectory, but for the SwitchMappings in my Program.cs I don't know what value would work. Here is my code:

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, builder) =>
    {
        builder
            .AddJsonFile("appsettings.json", false, false)
            .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, false)
            .AddEnvironmentVariables()
            .AddCommandLine(source =>
            {
                source.Args = args;
                source.SwitchMappings = new Dictionary<string, string>()
                {
                    { "-i", "FileReader__InputDirectory" } // <-- what is the correct mapping here?
                };
            });
    })
    .ConfigureServices((context, services) =>
    {
        Console.WriteLine(context.Configuration.GetSection("FileReader")["InputDirectory"]);
        services.AddHostedService<Worker>();
    })
    .Build();

When I am running dotnet run -i ./source/json I would expect the printed out value to be source/json (the value from the argument) but what I get is tmp (the value from the appsettings.json file).

If I try this without the nesting with a property on the appsettings root level it works just fine.

Daniel Lerps
  • 5,256
  • 3
  • 23
  • 33
  • What you ask is the default behavior. You only need to specify the *correct* key that includes the entire setting path. The host already sets up configuration to load settings from JSON, environment variables and CLI arguments, with later sources overriding previous ones. – Panagiotis Kanavos Jun 30 '23 at 09:47
  • For CLI arguments you need a switch mapping to tell which shorthand (in my case `-i`) overrides which configuration value. I know about the override structure, but I do not know how to set it up, that `-i` overrides the correct value. – Daniel Lerps Jun 30 '23 at 09:51
  • 1
    The title didn't ask how to change the mapping, only how to override settings from the CLI. If you want to build your own CLI arguments a **far** better option is to use the [CommandLine](https://learn.microsoft.com/en-us/dotnet/standard/commandline/) package and provide commands, options, arguments, help, defaults etc. The CLI configuration provider is built to override config settings, not create a nice CLI – Panagiotis Kanavos Jun 30 '23 at 10:02

2 Answers2

1

Use : as delimiter:

source.SwitchMappings = new Dictionary<string, string>()
{
     { "-i", "FileReader:InputDirectory" } 
};

__ is substitution for : specifically for environment variables.

Note that you can just pass it via CLI parameter (see this answer):

dotnet run --FileReader:InputDirectory=./source/json

See also:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
1

It seems the real question is how to add a new argument mapping, not how to override settings.

All config sources are available through the ConfigurationBuilder.Sources list. The configuration code could find the existing source and change its mapping :

.ConfigureAppConfiguration((context, builder) =>
{
    var cliSource=builder.Sources.OfType<CommandLineConfigurationSource>();
    if(cliSource!=null)
    {
        cliSource.SwitchMappings=new Dictionary<string, string>()
                {
                    { "-i", "FileReader:InputDirectory" }
                };
    }
}

Remove the entire ConfigureAppConfiguration section and use --FileReader:InputDirectory in the command line to override the JSON setting.

dotnet run --FileReader:InputDirectory=tmp

The generic host configures JSON, environment variables and CLI arguments out of the box. There's no need to add the same sources all over again.

The Command Line arguments section in ASP.NET Core configuration shows how to pass arguments using various delimiters like --, = or /, eg :

dotnet run /MyKey "Using /" /Position:Title=Cmd /Position:Name=Cmd_Rick

or

dotnet run --MyKey "Using --" --Position:Title=Cmd --Position:Name=Cmd_Rick

In your case, you can use

dotnet run --FileReader:InputDirectory=tmp
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Thanks for your answer. My question was more specific towards the shorthand configuration (`-i` in the example). I updated the title to make it more clear. – Daniel Lerps Jun 30 '23 at 10:00
  • The question asked how to override settings, not the mapping. I suspect you can change the mapping *without* having to specify all sources again. The CLI config provider wasn't built to create usable CLIs though, only to override settings. If you want a nice CLI, with help, descriptions, defaults etc you should check one of the many CLI packages, including System.CommandLine – Panagiotis Kanavos Jun 30 '23 at 10:04
  • @DanielLerps you should be able to add switch mappings to the existing CommandLineConfigurationSource instead of adding it again – Panagiotis Kanavos Jun 30 '23 at 10:21