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