System.CommanLine 将 services.UseHostedService<T>() 添加到 commandHandler

问题描述 投票:0回答:1

我目前正在尝试实施一些工人服务。如果我可以告诉它基于命令行添加服务,那就太好了。

假设我使用命令“Background --service1 --service2”。 我希望命令处理程序将服务添加到主机。假设我在没有任何参数的情况下运行它,它会立即运行并停止。

我当前的实现无法工作,因为服务对象在添加到托管服务时始终为空,因为命令行尚未解析。

我能想象这种工作的唯一方法是分离命令行和主机构建器。然后从命令行获取结果并使用它来构建构建器。但我希望有更好的方法来做到这一点,因为有一个用于 System.CommandLine 的主机包。

    using System.CommandLine;
    using System.CommandLine.Builder;
    using System.CommandLine.Hosting;
    using System.CommandLine.Parsing;

    class Program
    {
        public static async Task<int> Main(string[] args)
        {

            IServiceCollection commandServices = new ServiceCollection();
            var parser = BuildCommandLine(ref commandServices)
                .UseHost(_ => Host.CreateDefaultBuilder(args)
                    .ConfigureServices((context, services) =>
                    {
                        ConfigureServices(context, services, commandServices);
                        AddLogging(context, services);
                    }),
                    hostBuilder =>
                    {
                        hostBuilder.UseCommandHandler<BackgroundCommand,  BackgroundCommand.BackgroundCommandHandler>();
                    })
                .UseDefaults()  // Ensure defaults are added last in the chain
                .Build();

            return await parser.InvokeAsync(args);

            static CommandLineBuilder BuildCommandLine(ref IServiceCollection services)
            {
                // Create root command with description
                var rootCommand = new RootCommand("This is your command-line app's root command.");

                var backgroundCommand = new BackgroundCommand();
                backgroundCommand.Handler = new BackgroundCommand.BackgroundCommandHandler(ref services);
                rootCommand.AddCommand(backgroundCommand);

                return new CommandLineBuilder(rootCommand);
            }
        }
    }
public class BackgroundCommand : Command
{
    public BackgroundCommand()
        : base("Background", "Runs background workers based on options")
    {
        var service1Option= new Option<bool>(aliases: ["--service1"], description: "", getDefaultValue: () => false);
        var service2Option= new Option<bool>(aliases: ["--service2"], description: "", getDefaultValue: () => false);

        AddOption(service1Option);
        AddOption(service2Option);
    }
    public class BackgroundCommandHandler : ICommandHandler
    {
        private readonly IServiceCollection _services;
        public bool Service1 { get; set; } = false;
        public bool Service2 { get; set; } = false;

        public BackgroundCommandHandler(ref IServiceCollection services)
        {
            _services = services;
        }

        public int Invoke(InvocationContext context)
        {
            throw new NotImplementedException();
        }

        public async Task<int> InvokeAsync(InvocationContext context)
        {
            if (Service1)
            {
                _services.AddHostedService<Service1Worker>();
            }   
            if(Service2)
            {
                _services.AddHostedService<Service2Worker>();
            }
            return 0;
        }
    }
}
c# .net-core command-line background-service system.commandline
1个回答
0
投票

所以我遇到的问题是这样的。 托管的工作方式是添加服务,然后运行命令行。因此,一旦解析了命令行,您就无法添加更多服务。

有一个简单的解决方法,那就是仅使用标准主机,并使用 CommandLineBuilder()。

Progam.cs

var builder = Host.CreateApplicationBuilder(args);
ConfigureWorkerServices(builder.Services, args);

void ConfigureWorkerServices(IServiceCollection services, string[] args)
{
    var commandLineBuilder = new CommandLineBuilder();

    commandLineBuilder.Command.AddCommand(new WorkerCommand(services));

    var parser = commandLineBuilder.Build();
    parser.InvokeAsync(args);
}

基本命令

    public abstract class BaseCommand<T> : Command where T : ICommandModel
    {
        public IServiceCollection Services { get; init; }

        public BaseCommand(string name, IServiceCollection services, string? description = null) : base(name, description)
        {
            Services = services;

            Handler = CommandHandler.Create<T>(ParseOptions);
        }

        /// <summary>
        /// This fills the Settings.
        /// </summary>
        /// <param name="model">Moddel to fill.</param>
        public abstract void ParseOptions(T model);
    }

后台命令

public abstract class BackgroundCommand : BaseCommand<BackgroundCommandModel>
{
    public Option<bool> Background = new Option<bool>(aliases: ["--Background", "--background", "-b", "-B"], description: "Run the process as a background task.", getDefaultValue: () => false);

    public BackgroundCommand(string name, IServiceCollection services, string? description = null) : base(name, services, description)
    {
        AddOption(Background);
    }

    public override void ParseOptions(BackgroundCommandModel model)
    {
        ArgumentNullException.ThrowIfNull(model);

        Services.AddSingleton<Processing>();
        if (model.Background)
        {
            Services.AddHostedService<ProcessingWorker>();
        }
        else
        {
            Services.AddHostedService<OneTimeRunHostedService<Processing>>();
        }
}}

后台命令模式

    public class BackgroundCommandModel : ICommandModel
    {
        public bool Background { get; set; }
    }

因此,在构建主机之前,您需要单独运行 CommandLineBuiler。这样您就可以注入您可能需要的所有服务。

© www.soinside.com 2019 - 2024. All rights reserved.