IConfiguration的命令行解析

本文介绍了J4JCommandLine,一个针对.NET的命令行解析器,旨在简化与IConfiguration系统的集成。J4JCommandLine允许从命令行、JSON配置文件、用户秘密等多源获取配置信息。它支持多种操作系统,并提供了灵活的属性绑定和日志记录功能。此外,文章还探讨了如何定义自定义转换器、处理集合和嵌套属性,以及如何提供帮助信息。
摘要由CSDN通过智能技术生成

目录

介绍

背景

使用代码

支持哪些操作系统?

您可以绑定哪些类型的属性?

绑定到集合

嵌套属性和公共无参数构造函数

提供帮助

日志记录

兴趣点


介绍

有许多可用于.NET的命令行解析器,其中一些可与MicrosoftIConfiguration API配合使用。我之所以写J4JCommandLine,是因为我对命令行解析在幕后的工作方式很感兴趣……而且我发现的解析器让我觉得难以配置。公平地说,这可能是因为它们比J4JCommandLine更灵活,允许您将命令行绑定到各种目标。

将解析器的范围限制在IConfiguration系统范围内,使其在目标对象方面的灵活性降低。但它得到的比失去的多(在我看来,至少 :))因为它现在可以从各种来源获取配置信息:命令行、JSON 配置文件、用户机密等等。

背景

最初,J4JCommandLine只是另一个命令行解析器,因为我对LL(1)解析器很好奇并想写一个。

“LL(1)”表示解析器从左到右扫描标记,并且只向前看1个标记(第二个“L是关于在树数据结构中遵循/构造左手路由;我对此并不确定,因为我没有在J4JCommandLine中使用基于树的方法)

从技术上讲,J4JCommandLine不是 LL(1) 解析器,因为它在解析之前对生成的令牌进行了一些预处理。主要的此类步骤是将起始引用者标记和结束引用者标记之间的所有标记合并为单个测试标记。

通过查阅Github文档,您可以从J4JCommandLine概念上了解它如何解析命令行的更多信息。但就目前而言,对我来说关键的里程碑是,在它开始工作后,我意识到有一种方法可以让它与微软的IConfiguration系统一起工作。

您可以通过创建ConfigurationBuilder的实例、向其添加(配置信息的)提供者然后调用其Build()方法来创建IConfiguration(技术上,IConfigurationRoot)的实例:

var parser = testConfig.OperatingSystem.Equals( "windows", StringComparison.OrdinalIgnoreCase )
                ? Parser.GetWindowsDefault( logger: Logger )
                : Parser.GetLinuxDefault(logger: Logger);

_options = parser.Collection;

_configRoot = new ConfigurationBuilder()
                .AddJsonFile("some file path")
                .AddUserSecrets<ConfigurationTests>()
                .AddJ4JCommandLine(
                    parser,
                    out _cmdLineSrc, 
                    Logger )
                .Build();

在幕后,向提供者查询属性名称和值的键/值对。当您从IConfiguration系统请求对象时,这些用于初始化对象:

var configObject = _configRoot.Get<SomeConfigurationObject>();

J4JCommandLine的解析器与其提供者一起工作,将命令行文本转换为配置值。

使用代码

J4JCommandLine非常可配置,但基本用法非常简单,前提是默认设置适合您。您需要做的就是将提供程序添加到您的ConfigurationBuilder实例中,调用Build()并执行。

但是,这不会导致您的命令行被解析......因为您必须告诉解析器它可能遇到哪些命令行选项、它们是什么类型、是否需要它们等等。您可以通过绑定属性来做到这一点将您的configuration对象转换为命令行标记。

这是一个使用简单configuration对象的示例:

public class Configuration
{
    public int IntValue { get; set; }
    public string TextValue { get; set; }
}

在您的启动代码中,您将执行以下操作:

var config = new ConfigurationBuilder()
    .AddJ4JCommandLineForWindows( out var options, out _ )
    .Build();

options!.Bind<Configuration, int>(x => Program.IntValue, "i")!
    .SetDefaultValue(75)
    .SetDescription("An integer value");

options.Bind<Configuration, string>(x => Program.TextValue, "t")!
    .SetDefaultValue("a cool default")
    .SetDescription("A string value");

options.FinishConfiguration();

现在当你调用时:

var parsed = config.Get<Configuration>();

结果Configuration对象将反映命令行参数。

还有一种TryBind<>()方法可以用来代替Bind,因此您不必检查返回值是否为非null-(这表明无法完成绑定)。为了简单起见,我没有在示例中包含对null代码的检查。

支持哪些操作系统?

我试图让J4JCommandLine操作系统不可知,因为这些天.NET可以在非Windows平台上运行。坦率地说,我没有在Windows以外的任何环境下测试过它……但支持其他系统的逻辑就在那里。 

您可以控制J4JCommandLine操作系统的工作方式,方法是告诉它要使用哪些词法元素以及命令行键(例如,/x中的x)是否区分大小写。在幕后,这是AddJ4JCommandLineForWindows()AddJ4JCommandLineForLinux()扩展方法所做的一部分:它们将那些操作系统特定的参数设置为合理的默认值。

以下是默认值:

操作系统默认值
 

 

词汇元素

按键大小写敏感?

Windows

引号:"'
键前缀:/
分隔符:[空格] [制表符]
值前缀:=

Linux

引用者:" '
键前缀:- --
分隔符:[空格] [制表符]
值前缀:=

引文定义了应被视为单个块的文本元素。键前缀表示选项键的开始(例如,/x-x)。分隔符在命令行上分隔标记。值前缀(我很少使用它,因为常规分隔符似乎工作得很好)将选项键链接到文本元素(例如,/x=abc)。

请记住,当有多个有效的键前缀(例如,---)时,任何一个键都可以使用。所以-a, --ALongerKey, -ALongerKey--a就目前J4JCommandLine而言都是有效的。这不是“Linux方式,但我还没有弄清楚如何正确地做事。

您可以绑定哪些类型的属性?

J4JCommandLine无法将命令行选项绑定到任何随机C#类型。它必须能够将一个或多个文本值转换为目标类型的实例,这需要有一种可用的转换方法。在这方面,它与API本身没有什么不同IConfiguration,它依赖于C#的内置Convert类将文本转换为类型实例。

但是,在转换方面J4JCommandLine是可扩展的;您可以定义自己的转换器方法,将文本更改为自定义类型的实例。有关详细信息,请参阅Github文档...并请记住,我的代码库的一部分没有经过充分测试。

实际上,我怀疑您是否需要定义自己的转换器。默认情况下,J4JCommandLine可以转换任何在C#的内置Convert类中具有相应方法的类型(实际上,J4JCommandLine只是简单地包装这些方法以满足其特定需求)。

绑定到集合

除了能够绑定到最常用的配置类型之外,J4JCommandLine还可以绑定到这些类型的某些类型的集合:

  • 支持类型的数组(例如,string[]
  • 支持的类型列表(例如,List<string>

IConfiguration系统还允许您绑定到Dictionaries。但是我还没有弄清楚如何在命令行的上下文中使其工作。

对于可以绑定的内容,还有另一个不太明显的限制。J4JCommandLine可以绑定到本机的enums,甚至flagged enums(即,enums[Flag] attribute标记)。但它不能绑定到flagged enums。这是因为它无法判断是否enum应该将与值对应的文本标记序列连接成一个OR结果,还是应该将其视为 unconcatenated enums集合。

嵌套属性和公共无参数构造函数

J4JCommandLineIConfiguration的要求相同,配置对象和被绑定的属性通常必须具有public parameterless constructors。这是因为IConfiguration API必须能够在内部创建实例,而无需任何实际操作的知识。

但是,该要求有一个例外:如果配置对象的构造函数逻辑负责初始化属性,则这些属性不需要public parameterless constructors。这是一个例子:

public class EmbeddedTargetNoSetter
{
    public BasicTargetParameteredCtor Target1 { get; } = new( 0 );
    public BasicTargetParameteredCtor Target2 { get; } = new( 0 );
}

public class BasicTargetParameteredCtor
{
    private readonly int _value;

    public BasicTargetParameteredCtor( int value )
    {
        _value = value;
    }

    public bool ASwitch { get; set; }
    public string ASingleValue { get; set; } = string.Empty;
    public List<string> ACollection { get; set; } = new();
    public TestEnum AnEnumValue { get; set; }
    public TestFlagEnum AFlagEnumValue { get; set; }
}

即使该类型BasicTargetParameteredCtor没有 public parameterless constructor,您仍然可以绑定到Target1Target2属性,因为它们是由EmbeddedTargetNoSetter的构造函数逻辑初始化的(在这种情况下,通过这些new()调用隐式地进行初始化)。

这个例子还强调了J4JCommandLine的另一件事:您可以绑定到嵌套属性:

Bind<EmbeddedTargetNoSetter, bool>( _options!, x => x.Target1.ASwitch, testConfig );
Bind<EmbeddedTargetNoSetter, string>( _options!, x => x.Target1.ASingleValue, testConfig );
Bind<EmbeddedTargetNoSetter, TestEnum>( _options!, x => x.Target1.AnEnumValue, testConfig );
Bind<EmbeddedTargetNoSetter, TestFlagEnum>
    ( _options!, x => x.Target1.AFlagEnumValue, testConfig );
Bind<EmbeddedTargetNoSetter, List<string>>
    ( _options!, x => x.Target1.ACollection, testConfig );

提供帮助

用户在命令行上提供无效参数的情况并不少见。让他们知道他们应该输入的内容很重要。J4JCommandLine通过为您提供一个界面来解决这个问题,您可以使用该界面来显示您与绑定选项相关联的帮助信息。您可以这样做:

options!.Bind<Program, int>(x => Program.IntValue, "i")!
    .SetDefaultValue(75)
    .SetDescription("An integer value")
    .IsOptional();

options.Bind<Program, string>(x => Program.TextValue, "t")!
    .SetDefaultValue("a cool default")
    .SetDescription("A string value")
    .IsRequired();

options.FinishConfiguration();

var help = new ColorHelpDisplay(new WindowsLexicalElements(), options);
help.Display();

你通过调用扩展方法来描述一个选项。这些允许您设置默认值,描述它的用途,并指示它是必需的还是可选的(默认是可选的)。

当然,通常情况下,您不会总是像这段代码片段那样显示帮助信息。只有在用户要求时(例如,通过设置命令行选项)或创建的任何配置对象出现问题时,您才会显示帮助。

请注意,当它检测到问题时J4JCommandLine不会自动显示帮助。随你决定。

J4JCommandLine程序集包括一个普通的帮助显示引擎,称为DefaultHelpDisplay。但我通常更喜欢使用包含在github存储库中的ColorfulHelp库提供的更丰富多彩的显示。无论您使用哪个(或者如果您自己使用),您都必须使用有关正在使用的词汇元素(即特殊字符,如/ ? = “)和您定义的选项集合的信息来配置帮助系统。这样,帮助系统就可以提取和显示有关如何指定/格式化选项、它们的默认值、是否需要它们等信息。

日志记录

J4JCommandLine使用我的配套库J4JLogger来记录问题。日志记录功能是可选的——默认情况下,不记录任何内容。但是,如果您想知道J4JCommandLine内部发生了什么,那么将其包括在内是一件好事。

兴趣点

当我开始深入研究该IConfiguration系统的工作原理时,我希望能找到与我构建J4JCommandLine转换功能的方式类似的东西。有趣的是,我找不到它。相反,看起来IConfiguration API只是依赖于内置Convert类,如果没有可用的转换方法,则会失败。

类似地,IConfiguration系统在非结构化意义上进行检查(例如,对于具有public无参数构造函数的属性类型)。似乎没有一组(可能可扩展的)规则。相反,检查只是硬连线到代码库中。

https://www.codeproject.com/Articles/5314148/Command-Line-Parsing-for-IConfiguration

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值