Compartilhar via


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

Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de pré-lançamento que poderão ser substancialmente modificados antes do lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das informações aqui fornecidas.

Use um associador personalizado para injetar os tipos personalizados em um manipulador de comandos.

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

  • Os aplicativos de linha de comando geralmente são processos de curta duração, nos quais o custo de inicialização poderá ter um impacto perceptível no desempenho. Otimizar o desempenho é particularmente importante quando for necessário calcular as conclusões de tabulação. Os aplicativos de linha de comando são diferentes dos aplicativos Web e GUI, que tendem a ser processos de vida relativamente longa. O tempo de inicialização desnecessário não é apropriado para processos de curta duração.
  • Ao executar um aplicativo de linha de comando com vários subcomandos, apenas um desses subcomandos executará. Se um aplicativo configurar dependências para os subcomandos que não executarem, ele degradará desnecessariamente o desempenho.

Para configurar o DI, crie uma classe derivada de BinderBase<T> em que T está a interface para a qual você deseja injetar uma instância. Na substituição do método GetBoundValue, obtenha e retorne a instância que você quiser injetar. O exemplo a seguir injeta a implementação do agente 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 método SetHandler, passe para o lambda uma instância da classe injetada e passe uma instância da 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;
        }
    }
}

Confira também

System.CommandLine overview