Je travaille sur un POC pour une application console et j'ai du mal à récupérer les valeurs de la ligne de commande de la configuration après avoir utilisé AddCommandLine dans la configuration.

CSPROJ

<PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

Classe de programme

public static class Program
    {
        public static async Task Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .WriteTo.RollingFile("Logs//log.txt")
            .CreateLogger();

            await CreateHostBuilder(args)
                .Build()
                .RunAsync();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseSerilog()
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config.AddJsonFile("settings.json", true, true);
                    config.AddCommandLine(args);
                })
                .ConfigureServices((hostcontext, services) =>
                {
                    services.AddHostedService<ConsoleApp>();
                });
    }

Classe ConsoleApp

 public class ConsoleApp : IHostedService
    {
        private readonly IConfiguration config;
        private readonly ILogger<ConsoleApp> log;

        public ConsoleApp(IConfiguration configuration, ILogger<ConsoleApp> logger)
        {
            config = configuration;
            log = logger;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            var t = config.GetSection("Args");
            Parser.Default.ParseArguments<DeleteOptions>(t)
                .WithParsed<DeleteOptions>()
                .WithNotParsed();

            foreach (var c in config.AsEnumerable())
            {
                log.LogInformation($"{c.Key, -15}:{c.Value}");
            }


            log.LogInformation($"Completing Start Task");
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            log.LogInformation($"Complete End Task");

            return Task.CompletedTask;
        }
    }

La section Parser avant la boucle foreach ne compile pas et la sortie de la boucle n'imprime aucun des arguments que j'ai ajoutés.

Je suis conscient du conseil général selon lequel var someValue = Configuration.GetValue<int>("MySetting:SomeValue"); où l'argument est --MySetting=SomeValue est le moyen recommandé pour récupérer les valeurs de la ligne cmd.

Les valeurs que j'utilise comme paramètres sont delete -e CI -t depchpolestar -l de-DE et quand je regarde mon objet de configuration, je vois

enter image description here

C'est pourquoi je pense que la ligne var t = config.GetSection("Args"); devrait récupérer le tableau args. J'ai également essayé var t = config.GetValue<string[]>("Args"); mais aucun ne semble fonctionner. Il me semble que l'index 4 de l'objet de configuration est un tableau de chaînes codé par "Args"

Comment récupérer le tableau de chaînes pour pouvoir le transmettre à la méthode ParseArguments de CommandLineParser ?

[Modifier] Une solution :

Je peux maintenant faire passer les arguments, mais ce n'est pas une approche particulièrement agréable ; Si je construis l'argument en tant que --delete "-e CI -t depchpolestar -l de-DE" au lieu de delete -e CI -t depchpolestar -l de-DE et en ajoutant le code suivant à la classe ConsoleApp :

var args = config.GetValue<string>("delete");
            string[] arguments = null;
            if(!string.IsNullOrEmpty(args))
            {
                var tempArgs = args.Split(" ");
                arguments = new string[tempArgs.Length + 1];
                arguments[0] = "delete";
                for(int i = 0; i < tempArgs.Length; ++i)
                {
                    arguments[i + 1] = tempArgs[i];
                }
            }

            Parser.Default.ParseArguments<DeleteOptions>(arguments)
                .WithParsed<DeleteOptions>(async c => await c.Dowork())
                .WithNotParsed(HandleParseError);

L'exécution atteint la méthode DoWork. Bien mais DeleteOptions.cs définit un verbe et l'intention est d'ajouter plus de commandes. Donc plus de travail à faire mais en allant dans le bon sens.

[Modifier] J'ai également réalisé que je n'avais pas besoin d'ajouter l'appel AddCommandLine() car ils sont ajoutés par défaut.

3
onesixtyfourth 23 janv. 2020 à 12:33

1 réponse

Meilleure réponse

Ok il semble que j'ai trop compliqué cela et j'ai fini avec ça:

public static class Program
{
    public static async Task Main(string[] args)
    {
        var builtHost = CreateHostBuilder(args).Build();

        var console = builtHost.Services.GetService<ConsoleApp>();
        await console.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
         Host.CreateDefaultBuilder(args)
             .UseSerilog()
             .ConfigureAppConfiguration((hostingContext, config) =>
             {
                  config.AddJsonFile("settings.json", true, true);
                  config.AddCommandLine(args);
             })
             .ConfigureServices((hostcontext, services) =>
             {
                 services.AddTransient<ConsoleApp>();
             });
}

Et ceci en tant que méthode d'exécution dans ConsoleApp :

public Task Run()
{
    while (true)
    {
       var input = ReadFromConsole();
       if (string.IsNullOrWhiteSpace(input))
       {
           continue;
       }
       else if (input.ToLower().Equals("exit"))
       {
           break;
       }
       else
       {
              Parser.Default.ParseArguments<DeleteOptions, ConcatOptions,   DownloadOptions, ReportOptions>(input.Split(" "))
                        .WithParsed<DeleteOptions>(async options => await options.DoWork())
                        .WithParsed<ConcatOptions>(async options => await options.DoWork())
                        .WithParsed<DownloadOptions>(async options => await options.DoWork())
                        .WithParsed<ReportOptions>(async options => await options.DoWork())
                        .WithNotParsed(HandleParseError);
        }
  }

  return Task.CompletedTask;
}

Cela fonctionne très bien pour me permettre de l'utiliser comme une application de console interactive. J'ai un problème avec la DI. J'ai créé une classe OptionsBase qui a configuré la journalisation et je l'ai fait de cette façon car essayer d'ajouter un paramètre à l'une des classes Options échoue en indiquant qu'il est impossible de trouver le constructeur sans paramètre. Je suppose donc que CommandLine nécessite que les constructeurs par défaut fonctionnent. Obtenir un enregistreur comme je l'ai fait me donne plusieurs fichiers journaux, je dois donc résoudre ce problème.

0
onesixtyfourth 31 janv. 2020 à 12:40