dotnet / dotnet/command-line-api
System.Commandline DependencyInjection could be much better if the underlying BindingContext was easier to extend.
- Dominant language
- C#
- Stars
- 3.7k
- Forks
- 428
- PR merge metrics
- No merged PRs in 30d
Description
I'll share some code and describe my issue:
return await new ConsoleApplicationBuilder()
.UseServiceCollection(new ServiceCollection())
.GetConfiguration(() =>
{
var builder = new ConfigurationBuilder()
.SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
.AddJsonFile("appSettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appSettings.Development.json", optional: true, reloadOnChange: true);
var configuration = builder.Build();
return configuration;
})
.ConfigureServices((configuration, services) =>
{
services
.AddCommandHierarchy(builder =>
{
builder
.IsParentOf()
.IsParentOf()
.IsParentOf();
})
.AddLogging(configure =>
{
configure
.AddConsole(options => {
options.FormatterName = "nonamespace";
}).AddNoNamespaceConsoleFormatter(options =>
{
options.IncludeNamespace = false;
});
});
Bootstrap.Configure(configuration, services);
})
.ConfigureCommandLine(context =>
{
var applicationRootCommand = context.ServiceProvider.GetRequiredService().GetCommand();
var commandline = new CommandLineBuilder(applicationRootCommand)
.UseHost(Host.CreateDefaultBuilder)
.UseRuntimeLogLevel(() => GlobalParameters.LogLevel,
context => context.GetRequiredService().Services.GetRequiredService>(),
"CurseDownload")
.UseDefaults();
return commandline;
}).ExecuteAsync(args);
I want to to utilize a couple things with my application:
- config files
- runtime loglevel switch (to control verbosity of entire execution) (implemented as `CommandLineBuilder`middleware)
- constructor dependency injection in commands
unfortunately there are a couple struggles when doing this:
- if i want commands with constuctor injection i need to construct commands prior to building the `CommandLineBuilder` using an external ServiceCollection.
- If i want external ServiceConfiguration i cannot use the built in configuration loading from `System.CommandLine.Hosting`, but have to do it myself.
- If i add my service to `CommandLineBuilder`.ConfigureServices i depend on `System.CommandLine.Hosting` to go through `GetRequiredService.Services.GetRequiredService` which feels really awkward depency wise (when developing packages to help with console applications. Suddenly you require the dependency on hosting, even though all you need is the `IHost` to get the service.
- If i add services to an external serviceProvider i will still be unable to access my service through the `BindingContext` unless i add a dependency to `System.CommandLine.Hosting` and repeat service registration.
Unfortunately DI scenarios still feel rather awkward from an extensibility point of view, and the BindingBase solution was something i absolutely did not want to bother with.
Ideally there should be an API to extend the pool of services to retrieve services from a binding context (without depending on `System.CommandLine.Hosting`)
Like adding a method like `AddServiceProvider` to `CommandLineBuilder` would solve all my issues if that lead to the binding context delegating to a given additional serviceProvider as fallback.
Would that perhaps be an option?
Contributor guide
Assessment
This issue has not been assessed yet.