dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.38k stars 380 forks source link

Will running an Api inside handler cause any problems? #2414

Closed doddi321 closed 5 months ago

doddi321 commented 5 months ago

Hi, I have a service that I want to function as an Api and run some other processes like this:

using System.CommandLine;
using TestService.Common.Api;
using TestService.Common.Kafka;
using TestService.Common.Registration;

var topicOption = new Option<string[]>(
    name: "--consumers",
    description: "Names of the consumers to run.")
{
    IsRequired = true,
    AllowMultipleArgumentsPerToken = true
};

var rootCommand = new RootCommand("Test Service");

var apiCommand = new Command("api", "REST Api");
var consumerCommand = new Command("kafka", "Kafka consumers");

consumerCommand.AddOption(topicOption);
consumerCommand.SetHandler((context) =>
{
    var topic = context.ParseResult.GetValueForOption(topicOption);
    Console.WriteLine($"consumer stuff with topic {string.Join(", ", topic)}");
});

apiCommand.SetHandler(f =>
{
        var builder = WebApplication.CreateBuilder(args);

        builder.Services
            .AddApi()
            .AddCommon(builder.Configuration)
            .AddKafka(builder.Configuration)
            .AddProducer<HelloWorldEvent>("hello-world");

        var app = builder.Build();

        app.UseSwagger();
        app.UseSwaggerUI();

        app.UseHttpsRedirection();

        app.MapPost("/helloworld", async (KafkaProducer<HelloWorldEvent> producer) =>
            {
                var message = new HelloWorldEvent
                {
                    Id = Guid.NewGuid().ToString(),
                };

                await producer.ProduceAsync(message.Id, message);

                return "ok";
            })
            .WithName("HelloWorld")
            .WithOpenApi();

        app.Run();
    });

rootCommand.AddCommand(apiCommand);
rootCommand.AddCommand(consumerCommand);

await rootCommand.InvokeAsync(args);

Will running an Api inside a handler cause some unexpected problems?

KalleOlaviNiemitalo commented 5 months ago

I expect that will be OK.

If the application had set up a SynchronizationContext before rootCommand.InvokeAsync, then that could cause problems with ASP.NET Core; but your code doesn't do that.

You could also get a CancellationToken from System.CommandLine and gracefully shut down the host when cancellation is requested. This seems to have a risk of deadlock on .NET Framework (https://github.com/dotnet/command-line-api/issues/832), but modern versions of ASP.NET Core aren't supported on .NET Framework anyway.

KathleenDollard commented 5 months ago

It will add a bit to your callstack. That isn't a problem, but you'll see it.

You could also set a flag you evaluate after parsing. We do that in some of the tests. While I do not think what you are doing is likely to have problems, it would pull the significant code you have in the handler in a place in your code you could might be able to manage it better, from a human standpoint, and possibly test it better,

doddi321 commented 5 months ago

@KalleOlaviNiemitalo @KathleenDollard Thanks for the feedback and observations!