Como configurar a injeção de dependência em System.CommandLine

Importante

System.CommandLine está atualmente em PREVIEW, e esta documentação é para a versão 2.0 beta 4. Algumas informações estão relacionadas ao produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado. A Microsoft não faz garantias, de forma expressa ou implícita, em relação à informação aqui apresentada.

Use um fichário personalizado para injetar tipos personalizados em um manipulador de comandos.

Recomendamos a injeção de dependência específica do manipulador (DI) pelos seguintes motivos:

  • Os aplicativos de linha de comando geralmente são processos de curta duração, nos quais o custo de inicialização pode ter um impacto notável no desempenho. A otimização do desempenho é particularmente importante quando os preenchimentos de guias precisam ser calculados. Os aplicativos de linha de comando são diferentes dos aplicativos Web e GUI, que tendem a ser processos relativamente longos. O tempo de inicialização desnecessário não é apropriado para processos de curta duração.
  • Quando um aplicativo de linha de comando com vários subcomandos é executado, apenas um desses subcomandos será executado. Se um aplicativo configura dependências para os subcomandos que não são executados, ele degrada desnecessariamente o desempenho.

Para configurar DI, crie uma classe que derive de BinderBase<T> onde T é a interface para a qual você deseja injetar uma instância. Na substituição de GetBoundValue método, obtenha e retorne a instância que você deseja injetar. O exemplo a seguir injeta a implementação do registrador padrão para ILogger:

public class MyCustomBinder : BinderBase<ILogger>
{
    protected override ILogger GetBoundValue(
        BindingContext bindingContext) => GetLogger(bindingContext);

    ILogger GetLogger(BindingContext bindingContext)
    {
        using ILoggerFactory loggerFactory = LoggerFactory.Create(
            builder => builder.AddConsole());
        ILogger logger = loggerFactory.CreateLogger("LoggerCategory");
        return logger;
    }
}

Ao chamar o SetHandler método, passe para o lambda uma instância da classe injetada e passe uma instância da sua classe binder na lista de serviços:

rootCommand.SetHandler(async (fileOptionValue, logger) =>
    {
        await DoRootCommand(fileOptionValue!, logger);
    },
    fileOption, new MyCustomBinder());

O código a seguir é um programa completo que contém os exemplos anteriores:

using System.CommandLine;
using System.CommandLine.Binding;
using Microsoft.Extensions.Logging;

class Program
{
    static async Task Main(string[] args)
    {
        var fileOption = new Option<FileInfo?>(
              name: "--file",
              description: "An option whose argument is parsed as a FileInfo");

        var rootCommand = new RootCommand("Dependency Injection sample");
        rootCommand.Add(fileOption);

        rootCommand.SetHandler(async (fileOptionValue, logger) =>
            {
                await DoRootCommand(fileOptionValue!, logger);
            },
            fileOption, new MyCustomBinder());

        await rootCommand.InvokeAsync("--file scl.runtimeconfig.json");
    }

    public static async Task DoRootCommand(FileInfo aFile, ILogger logger)
    {
        Console.WriteLine($"File = {aFile?.FullName}");
        logger.LogCritical("Test message");
        await Task.Delay(1000);
    }

    public class MyCustomBinder : BinderBase<ILogger>
    {
        protected override ILogger GetBoundValue(
            BindingContext bindingContext) => GetLogger(bindingContext);

        ILogger GetLogger(BindingContext bindingContext)
        {
            using ILoggerFactory loggerFactory = LoggerFactory.Create(
                builder => builder.AddConsole());
            ILogger logger = loggerFactory.CreateLogger("LoggerCategory");
            return logger;
        }
    }
}

Consulte também

System.CommandLine Visão geral