dotnet / dotnet/command-line-api

.NET Generic Host and Dependency Injection

Open
#1,569 2 comments 5 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
3.7k
Forks
428
PR merge metrics
No merged PRs in 30d

Description

As best as I can tell, there doesn't seem to be a good way to automatically inject host services into SetHandler Func and Action; the only services injectable are those registered on the binding context.

```c#
public interface IFoo { }
public class Foo : IFoo { }

public class Program
{
public static async Task Main(string[] args)
{
var command = new RootCommand();

command.SetHandler((foo) =>
{
Console.WriteLine($"We have received an IFoo of type {foo.GetType()}");
});

var builder = new CommandLineBuilder(command);
var runner = builder.UseHost(
_ => Host.CreateDefaultBuilder(args),
hostBuilder =>
{
hostBuilder.ConfigureServices(services =>
{
services.AddSingleton();
});
})
.Build();

await runner.InvokeAsync(args);
}
}
```

The above example will always result in an argument exception, complaining about not being able to find a service of type ```IFoo```. This is because ```BindingContext``` only consults its internal set of registered / parsed services.

I couldn't figure out a way to make this work without doing things like manually injecting services into the ```BindingContext``` as part of an invocation middleware, or fetching the ```InvocationContext```, using that to retrieve the IHost instance, and then using the retrieved IHost instance to resolve my services within my handler. This seems messy.

I hacked together a change with a few tests (more are needed) to improve this scenario; I added the ability to register additional ```IServiceProvider``` instances to the ```BindingContext```, modified ```BindingContext``` to consult these additional instances when handling calls to GetService, and then added an extension method in ```System.CommandLine.Hosting``` that wires up the host ```IServiceProvider``` instance to the binding context as part of middleware.

I thought this might be a useful addition, a draft PR is here. I also thought I might be completely off base here, and there is a better way of doing this. 😄 I am happy with either outcome.

Draft PR https://github.com/dotnet/command-line-api/pull/1568

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.