如何在 System.CommandLine 中配置依赖关系注入

重要

System.CommandLine 目前为预览版,本文档适用于版本 2.0 beta 4。 一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。

使用自定义绑定器将自定义类型注入命令处理程序。

我们推荐特定于处理程序的依赖关系注入 (DI),原因如下:

  • 命令行应用通常是短期进程,其中启动成本可能会显著影响性能。 当必须计算 Tab 自动补全时,优化性能尤其重要。 命令行应用与 Web 和 GUI 应用不同,后者往往是相对长期的进程。 不必要的启动时间不适合短期进程。
  • 运行具有多个子命令的命令行应用时,将只会执行其中一个子命令。 如果应用为未运行的子命令配置依赖关系,则它没有必要降低性能。

若要配置 DI,请创建一个派生自 BinderBase<T> 的类,其中 T 是要为其注入实例的接口。 在 GetBoundValue 方法重写中,获取并返回要注入的实例。 以下示例为 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;
    }
}

调用 SetHandler 方法时,将注入类的实例传递给 lambda,并在服务列表中传递绑定器类的实例:

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

以下代码是包含上述示例的完整程序:

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

请参阅

System.CommandLine 概述