dotnet / dotnet/command-line-api
Filling Option default values from IConfiguration using Host
- Dominant language
- C#
- Stars
- 3.7k
- Forks
- 428
- PR merge metrics
- No merged PRs in 30d
Description
I want to fill the default values from some of my options from configuration stack. Since when using `UseHost()` the `Host` instance is built within the middleware, my `Command` classes have no access to the `Host` and it's configuration stack while defining options.
This is related to #1058 and #826.
I tried to extract this functionality into another middleware (calling RootCommand.Configure()), but the default values are determined before my middleware gets in action, so the value of `option1` is still `"abc"` and not affected by settings.json.
```c#
static void Main(string[] args)
{
var rootCommand = new RootCommand();
var commandLineParser =
new CommandLineBuilder(rootCommand)
.UseHost(hostBuilder =>
hostBuilder.ConfigureAppConfiguration(builder =>
{
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("settings.json", true, true)
.AddEnvironmentVariables();
}))
.UseMiddleware(context => { rootCommand.Configure(context); })
.UseDefaults()
.Build();
commandLineParser.Invoke(args);
}
```
```c#
public class RootCommand : System.CommandLine.RootCommand
{
public RootCommand()
{
Add(Option1);
Handler = CommandHandler.Create(Handle);
}
public Option Option1 { get; } = new("--option1", () => "abc", "first option");
public void Configure(InvocationContext context)
{
Option1.SetDefaultValueFactory(() => context.GetHost().Services.GetRequiredService().GetValue("DefaultOption1"));
}
public void Handle(string option1, IHost host)
{
Console.WriteLine(option1);
}
}
```
A nice solution were to allow somehow DI in the constructor of my `RootCommand`.
For now the only solution I have is to bypass `UseHost()` and create a host myself, passing it to the `RootCommand` constructor and simply adding the `Host` to Commandlines DI system. Alternatively just saving a reference to the host in a field and ignoring Commandline totally from the view of the `Host`. But both lack of some functionality of `UseHost()` like config directives and `InvocationLifetime`.
```c#
static void Main(string[] args)
{
var host = new HostBuilder()
.ConfigureAppConfiguration(builder =>
{
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("settings.json", true, true)
.AddEnvironmentVariables();
})
.Build();
var rootCommand = new RootCommand(host);
var commandLineParser =
new CommandLineBuilder(rootCommand)
.UseMiddleware(context => { context.BindingContext.AddService(_ => host); })
.UseDefaults()
.Build();
commandLineParser.Invoke(args);
}
```
```c#
public class RootCommand : System.CommandLine.RootCommand
{
public RootCommand(IHost host)
{
Add(Option1);
Option1.SetDefaultValueFactory(() => host.Services.GetRequiredService().GetValue("DefaultOption1"));
Handler = CommandHandler.Create(Handle);
}
public Option Option1 { get; } = new("--option1", () => "abc", "first option");
public void Handle(string option1, IHost host)
{
Console.WriteLine(option1);
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.