farlee2121.System.CommandLine.PropertyMapBinder 1.0.0

dotnet add package farlee2121.System.CommandLine.PropertyMapBinder --version 1.0.0                
NuGet\Install-Package farlee2121.System.CommandLine.PropertyMapBinder -Version 1.0.0                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用 NuGet 模块的 Install-Package 版本。
<PackageReference Include="farlee2121.System.CommandLine.PropertyMapBinder" Version="1.0.0" />                
对于支持 PackageReference 的项目,请将此 XML 节复制到项目文件中以引用包。
paket add farlee2121.System.CommandLine.PropertyMapBinder --version 1.0.0                
#r "nuget: farlee2121.System.CommandLine.PropertyMapBinder, 1.0.0"                
#r 指令可以用在 F# Interactive 和 Polyglot Notebooks 中。将此复制到交互式工具或脚本的源代码中以引用包。
// Install farlee2121.System.CommandLine.PropertyMapBinder as a Cake Addin
#addin nuget:?package=farlee2121.System.CommandLine.PropertyMapBinder&version=1.0.0

// Install farlee2121.System.CommandLine.PropertyMapBinder as a Cake Tool
#tool nuget:?package=farlee2121.System.CommandLine.PropertyMapBinder&version=1.0.0                

System.CommandLine.PropertyMapBinding

alternate text is missing from this package README image

动机/这是什么

目标是创建一个直观的处理器绑定体验,针对 System.CommandLine。一些子目标包括

  • 直观绑定复杂类型
  • 混合多个绑定规则以提供可定制且一致的绑定体验
  • 简单地扩展绑定管道
  • 支持将处理器的声明作为自包含的表达式(不引用符号实例)

示例

所有示例都假定以下定义可用

Option<int> frequencyOpt = new Option<int>(new string[] { "--frequency", "-f" }, "such description");

RootCommand rootCommand = new RootCommand("Test Test")
{
    new Argument<string>("print-me", "gets printed"),
    frequencyOpt, 
    new Option<IEnumerable<int>>(new string[] { "--list", "-l" }, "make sure lists work")
    {
        Arity = ArgumentArity.ZeroOrMore
    };
};

public static async Task SuchHandler(SuchInput input)
{
    Console.WriteLine($"printme: {input.PrintMe}; \nfrequency: {input.Frequency}; \nlist:{string.Join(",",input.SuchList)}");
}

public class SuchInput {
    public int Frequency { get; set; }
    public string? PrintMe { get; set; }
    public IEnumerable<int> SuchList { get; set; } = Enumerable.Empty<int>();
}

管道

骨干构造是 BinderPipeline

rootCommand.Handler = new BinderPipeline<SuchInput>()
    .MapFromName("print-me", model => model.PrintMe)
    .MapFromReference(frequencyOpt, model => model.Frequency)
    .MapFromName("-l", model => model.SuchList)
    .ToHandler(SuchHandler);

BinderPipeline 实际上是 IPropertyBinder 的集合。每个 IPropertyBinder 定义将输入分配给目标对象的策略。管道按给定顺序执行每个绑定器。这意味着后续的绑定器将覆盖先前的绑定器。这也意味着我们可以

  • 使用多个规则来绑定属性
  • 为任何给定的属性定义优先级/回退链

混合约定

管道可以处理许多绑定输入的方法。以下是一个使用简单的命名约定和显式映射回退的示例

rootCommand.Handler = new BinderPipeline<SuchInput>()
    .MapFromNameConvention(NameConvention.PascalCaseComparer)
    .MapFromName("-l", model => model.SuchList)
    .ToHandler(SuchHandler);

可以为此管道添加更多约定。以下是一些我没有实现但很容易添加的情况:

  • 从配置中映射默认值
  • 询问用户任何缺失的输入
    • 可以使用现有的设置器重载来完成,但可以使用类似于 .PromptIfMissing(name, selector) 的签名来自动化提示
  • 根据类型匹配属性

参见如何扩展获取更多详细信息。

绑定到现有模型

有时我们可能希望将我们的输入模型与输入绑定过程分离初始化(例如,从配置中默认模型)。

这很简单

SuchInput existingModelInstance = //...
rootCommand.Handler = new BinderPipeline<SuchInput>()
    .ToHandler(SuchHandler, existingModelInstance);

使用所需数据初始化模型

某些模型可能希望通过构造函数强制保证数据,或者某些字段在初始化后不允许修改。

这可以与System.Commandline的SetHandler类似处理。

IModelFactory<SuchInput> modelFactory = ModelFactory.FromSymbolMap((int frequency, string printMe) => new InputModel(frequency, printMe), frequencyOpt, printMeArg);
rootCommand.Handler = new BinderPipeline<SuchInput>()
    .ToHandler(SuchHandler, modelFactory);

这可以通过选项和参数别名实现相同的操作

IModelFactory<SuchInput> modelFactory = ModelFactor.FromNameMap((int frequency, string printMe) => new InputModel(frequency, printMe), "-f", "print-me");
rootCommand.Handler = new BinderPipeline<SuchInput>()
    .ToHandler(SuchHandler, modelFactory);

如何扩展

扩展管道相当简单。

核心合同是

public interface IPropertyBinder<InputModel>
{
    InputModel Bind(InputModel InputModel, InvocationContext context);
}

IPropertyBinder接受目标输入类的实例和由解析器提供的调用上下文。

输入定义(即选项和参数)可以在context.ParserResult.CommandResult.Symbol.Children中找到,可以通过诸如context.ParseResult.GetValueForOption之类的函数获取值。

存在关于符号名称和属性路径简单命名约定的示例。

另一个关键步骤是在BinderPipeline上注册扩展方法。需要考虑的主要行为

  • 扩展应将其绑定器添加到管道的末尾(例如,pipeline.Add(yourBinder)
  • 扩展应返回管道的修改副本(即始终具有返回类型BinderPipeline<T>
// Example pipeline extension
public static class BinderPipelineExtensions{
    public static BinderPipeline<InputModel> MapFromNameConvention<InputModel>(this BinderPipeline<InputModel> pipeline, NameConventionComparer comparer)
    {
        pipeline.Add(new NameConventionBinder<InputModel>(comparer)); // this adds an IPropertyBinder<T>
        return pipeline; // be sure to return the pipeline for further chaining
    }
}

如何处理依赖注入?

简而言之:在处理函数中调用依赖容器(即ToHandler(handlerFunction)

该库的位置是将依赖注入与模型绑定分离开来。一些原因包括

  • 保持两个活动的分离可以简化错误诊断并提高代码清晰度
  • 依赖容器可以很容易地在处理函数内部调用
  • 输入值可能需要与依赖容器注册,这需要在依赖容器之前完成输入模型
  • 输入模型应只为可能输入建模。它不负责组合或行为。
    • 处理函数的存在是为了在输入模型和消费者之间建立桥梁。

项目状态

一次成功的实验。核心构建器的体验可能稳定,但API可能会根据反馈/经验而更改。

该库可使用,已有测试,但没有保证(包括支持)。

产品 兼容和附加的计算目标框架版本。
.NET net5.0 已计算。 net5.0-windows 已计算。 net6.0 已计算。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。
.NET Core netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。
.NET Standard netstandard2.0 兼容。 netstandard2.1 已计算。
.NET Framework net461已计算。 net462已计算。 net463已计算。 net47已计算。 net471已计算。 net472已计算。 net48已计算。 net481已计算。
MonoAndroid monoandroid已计算。
MonoMac monomac已计算。
MonoTouch monotouch已计算。
Tizen tizen40已计算。 tizen60已计算。
Xamarin.iOS xamarinios已计算。
Xamarin.Mac xamarinmac已计算。
Xamarin.TVOS xamarintvos已计算。
Xamarin.WatchOS xamarinwatchos已计算。
兼容目标框架
包含的目标框架(在包中)
了解有关目标框架.NET Standard的更多信息。

NuGet包 (1)

显示依赖farlee2121.System.CommandLine.PropertyMapBinder的顶级1个NuGet包

下载
farlee2121.System.CommandLine.PropertyMapBinder.NameConventionBinder

一个用于通过简单的命名约定将控制台输入映射到属性的System.CommandLine.PropertyMapBinder扩展

GitHub仓库

此包没有被任何流行GitHub仓库使用。

版本 下载 最后更新
1.0.0 1,585 3/23/2022
1.0.0-preview1 1,061 3/5/2022
0.1.0-beta1 959 1/13/2022
0.1.0-alpha1 139 1/9/2022