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;
}
}
}