Skip to content

Commit 375b2c5

Browse files
authored
System.CommandLine beta 4 (#29580)
1 parent 8d732e4 commit 375b2c5

File tree

33 files changed

+196
-186
lines changed

33 files changed

+196
-186
lines changed

docs/standard/commandline/define-commands.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,12 @@ Options:
210210

211211
## Option and argument validation
212212

213-
For information about argument validation and how to customize it, see the following sections in the [Model binding](model-binding.md) article:
213+
For information about argument validation and how to customize it, see the following sections in the [Parameter binding](model-binding.md) article:
214214

215215
* [Built-in type and arity argument validation](model-binding.md#built-in-argument-validation)
216216
* [Custom validation and binding](model-binding.md#custom-validation-and-binding)
217217

218218
## See also
219219

220220
* [System.CommandLine overview](index.md)
221-
* [Model binding](model-binding.md)
221+
* [Parameter binding](model-binding.md)

docs/standard/commandline/dependency-injection.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: How to configure dependency injection in System.CommandLine
33
description: "Learn how to configure dependency injection in System.CommandLine."
4-
ms.date: 04/07/2022
4+
ms.date: 05/22/2022
55
no-loc: [System.CommandLine]
66
helpviewer_keywords:
77
- "command line interface"
@@ -13,7 +13,7 @@ ms.topic: how-to
1313

1414
[!INCLUDE [scl-preview](../../../includes/scl-preview.md)]
1515

16-
Use a [custom binder](model-binding.md#model-binding-more-than-16-options-and-arguments) to inject custom types into a command handler.
16+
Use a [custom binder](model-binding.md#parameter-binding-more-than-8-options-and-arguments) to inject custom types into a command handler.
1717

1818
We recommend handler-specific dependency injection (DI) for the following reasons:
1919

docs/standard/commandline/handle-termination.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: How to handle termination in System.CommandLine
33
description: "Learn how to handle termination in apps that are built with the System.Commandline library."
4-
ms.date: 04/07/2022
4+
ms.date: 05/24/2022
55
no-loc: [System.CommandLine]
66
helpviewer_keywords:
77
- "command line interface"
@@ -17,6 +17,8 @@ To handle termination, inject a <xref:System.Threading.CancellationToken> instan
1717

1818
:::code language="csharp" source="snippets/handle-termination/csharp/Program.cs" id="mainandhandler" :::
1919

20+
The preceding code uses a `SetHandler` overload that gets an [InvocationContext](model-binding.md#invocationcontext) instance rather than one or more `IValueDescriptor<T>` objects. The `InvocationContext` is used to get the `CancellationToken` and [ParseResult](model-binding.md#parseresult) objects. `ParseResult` can provide argument or option values.
21+
2022
Cancellation actions can also be added directly using the <xref:System.Threading.CancellationToken.Register%2A?displayProperty=nameWithType> method.
2123

2224
For information about an alternative way to set the process exit code, see [Set exit codes](model-binding.md#set-exit-codes).

docs/standard/commandline/model-binding.md

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: How to bind arguments to handlers in System.CommandLine
33
description: "Learn how to do model-binding in apps that are built with the System.Commandline library."
4-
ms.date: 04/07/2022
4+
ms.date: 05/24/2022
55
no-loc: [System.CommandLine]
66
helpviewer_keywords:
77
- "command line interface"
@@ -13,7 +13,7 @@ ms.topic: how-to
1313

1414
[!INCLUDE [scl-preview](../../../includes/scl-preview.md)]
1515

16-
The process of parsing arguments and providing them to command handler code is called *model binding*. `System.CommandLine` has the ability to bind many argument types built in. For example, integers, enums, and file system objects such as <xref:System.IO.FileInfo> and <xref:System.IO.DirectoryInfo> can be bound. Several `System.CommandLine` types can also be bound.
16+
The process of parsing arguments and providing them to command handler code is called *parameter binding*. `System.CommandLine` has the ability to bind many argument types built in. For example, integers, enums, and file system objects such as <xref:System.IO.FileInfo> and <xref:System.IO.DirectoryInfo> can be bound. Several `System.CommandLine` types can also be bound.
1717

1818
## Built-in argument validation
1919

@@ -45,7 +45,7 @@ This behavior can be overridden by setting <xref:System.CommandLine.Option.Allow
4545
myapp --item one --item two --item three
4646
```
4747

48-
## Model binding up to 16 options and arguments
48+
## Parameter binding up to 16 options and arguments
4949

5050
The following example shows how to bind options to command handler parameters, by calling <xref:System.CommandLine.Handler.SetHandler%2A>:
5151

@@ -66,11 +66,19 @@ The variables that follow the lambda represent the option and argument objects t
6666
* If the out-of-order options or arguments are of different types, a run-time exception is thrown. For example, an `int` might appear where a `string` should be in the list of sources.
6767
* If the out-of-order options or arguments are of the same type, the handler silently gets the wrong values in the parameters provided to it. For example, `string` option `x` might appear where `string` option `y` should be in the list of sources. In that case, the variable for the option `y` value gets the option `x` value.
6868

69-
There are overloads of <xref:System.CommandLine.Handler.SetHandler%2A> that support up to 16 parameters, with both synchronous and asynchronous signatures.
69+
There are overloads of <xref:System.CommandLine.Handler.SetHandler%2A> that support up to 8 parameters, with both synchronous and asynchronous signatures.
7070

71-
## Model binding more than 16 options and arguments
71+
## Parameter binding more than 8 options and arguments
7272

73-
To handle more than 16 options, or to construct a custom type from multiple options, create a *custom binder*. The binder lets you combine multiple option or argument values into a complex type and pass that into a single handler parameter. Suppose you have a `Person` type:
73+
To handle more than 8 options, or to construct a custom type from multiple options, you can use `InvocationContext` or a custom binder.
74+
75+
### Use `InvocationContext`
76+
77+
A <xref:System.CommandLine.Handler.SetHandler%2A> overload provides access to the <xref:System.CommandLine.Invocation.InvocationContext> object, and you can use `InvocationContext` to get any number of option and argument values. For examples, see [Set exit codes](#set-exit-codes) and [Handle termination](handle-termination.md).
78+
79+
### Use a custom binder
80+
81+
A custom binder lets you combine multiple option or argument values into a complex type and pass that into a single handler parameter. Suppose you have a `Person` type:
7482

7583
:::code language="csharp" source="snippets/model-binding/csharp/ComplexType.cs" id="persontype" :::
7684

@@ -92,7 +100,7 @@ There are <xref:System.Threading.Tasks.Task>-returning [Func](xref:System.Func%6
92100

93101
:::code language="csharp" source="snippets/model-binding/csharp/ReturnExitCode.cs" id="returnexitcode" :::
94102

95-
However, if the lambda itself needs to be async, you can't return a `Task<int>`. In that case, use <xref:System.CommandLine.Invocation.InvocationContext.ExitCode?displayProperty=nameWithType>. You can get the `InvocationContext` instance injected into your lambda just by including it as one of the parameters, as in the following example:
103+
However, if the lambda itself needs to be async, you can't return a `Task<int>`. In that case, use <xref:System.CommandLine.Invocation.InvocationContext.ExitCode?displayProperty=nameWithType>. You can get the `InvocationContext` instance injected into your lambda by using a SetHandler overload that specifies the `InvocationContext` as the sole parameter. This `SetHandler` overload doesn't let you specify `IValueDescriptor<T>` objects, but you can get option and argument values from the [ParseResult](#parseresult) property of `InvocationContext`, as shown in the following example:
96104

97105
:::code language="csharp" source="snippets/model-binding/csharp/ContextExitCode.cs" id="contextexitcode" :::
98106

@@ -181,32 +189,30 @@ Besides the file system types and `Uri`, the following types are supported:
181189
* `ulong`
182190
* `ushort`
183191

184-
## Inject System.CommandLine types
192+
## Use System.CommandLine objects
185193

186-
`System.CommandLine` allows you to use some types in handlers by adding parameters for them to the handler signature. The available types include:
194+
There's a `SetHandler` overload that gives you access to the <xref:System.CommandLine.Invocation.InvocationContext> object. That object can then be used to access other `System.CommandLine` objects. For example, you have access to the following objects:
187195

196+
* <xref:System.CommandLine.Invocation.InvocationContext>
188197
* <xref:System.Threading.CancellationToken>
189198
* <xref:System.CommandLine.IConsole>
190-
* <xref:System.CommandLine.Invocation.InvocationContext>
191199
* <xref:System.CommandLine.Parsing.ParseResult>
192200

193-
Other types can be injected by using custom binders. For more information, see [Dependency injection](dependency-injection.md).
201+
### `InvocationContext`
202+
203+
For examples, see [Set exit codes](#set-exit-codes) and [Handle termination](handle-termination.md).
194204

195205
### `CancellationToken`
196206

197207
For information about how to use <xref:System.Threading.CancellationToken>, see [How to handle termination](handle-termination.md).
198208

199209
### `IConsole`
200210

201-
<xref:System.CommandLine.IConsole> makes testing as well as many extensibility scenarios easier than using `System.Console`.
202-
203-
### `InvocationContext`
204-
205-
For an example, see [Set exit codes](#set-exit-codes).
211+
<xref:System.CommandLine.IConsole> makes testing as well as many extensibility scenarios easier than using `System.Console`. It's available in the <xref:System.CommandLine.Invocation.InvocationContext.Console?displayProperty=nameWithType> property.
206212

207213
### `ParseResult`
208214

209-
<xref:System.CommandLine.Parsing.ParseResult> is a singleton structure that represents the results of parsing the command line input. You can use it to check for the presence of options or arguments on the command line or to get the <xref:System.CommandLine.Parsing.ParseResult.UnmatchedTokens?displayProperty=nameWithType> property. This property contains a list of the [tokens](syntax.md#tokens) that were parsed but didn't match any configured command, option, or argument.
215+
The <xref:System.CommandLine.Parsing.ParseResult> object is available in the <xref:System.CommandLine.Invocation.InvocationContext.ParseResult?displayProperty=nameWithType> property. It's a singleton structure that represents the results of parsing the command line input. You can use it to check for the presence of options or arguments on the command line or to get the <xref:System.CommandLine.Parsing.ParseResult.UnmatchedTokens?displayProperty=nameWithType> property. This property contains a list of the [tokens](syntax.md#tokens) that were parsed but didn't match any configured command, option, or argument.
210216

211217
The list of unmatched tokens is useful in commands that behave like wrappers. A wrapper command takes a set of [tokens](syntax.md#tokens) and forwards them to another command or app. The `sudo` command in Linux is an example. It takes the name of a user to impersonate followed by a command to run. For example:
212218

docs/standard/commandline/snippets/customize-help/csharp/Program.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ static async Task Original(string[] args)
3737
foregroundColorOption
3838
};
3939

40-
rootCommand.SetHandler(
41-
(FileInfo file, bool lightMode, ConsoleColor color) =>
40+
rootCommand.SetHandler((file, lightMode, color) =>
4241
{
4342
Console.BackgroundColor = lightMode ? ConsoleColor.White: ConsoleColor.Black;
4443
Console.ForegroundColor = color;
@@ -77,8 +76,7 @@ static async Task First2Columns(string[] args)
7776
foregroundColorOption
7877
};
7978

80-
rootCommand.SetHandler(
81-
(FileInfo file, bool lightMode, ConsoleColor color) =>
79+
rootCommand.SetHandler((file, lightMode, color) =>
8280
{
8381
Console.BackgroundColor = lightMode ? ConsoleColor.Black : ConsoleColor.White;
8482
Console.ForegroundColor = color;
@@ -134,8 +132,7 @@ static async Task DescriptionSection(string[] args)
134132
foregroundColorOption
135133
};
136134

137-
rootCommand.SetHandler(
138-
(FileInfo file, bool lightMode, ConsoleColor color) =>
135+
rootCommand.SetHandler((file, lightMode, color) =>
139136
{
140137
Console.BackgroundColor = lightMode ? ConsoleColor.Black : ConsoleColor.White;
141138
Console.ForegroundColor = color;

docs/standard/commandline/snippets/customize-help/csharp/scl.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
1212
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
1313
<PackageReference Include="Spectre.Console" Version="0.43.0" />
14-
<PackageReference Include="System.CommandLine" Version="2.0.0-beta3.22114.1" />
14+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
1515
</ItemGroup>
1616

1717
</Project>

docs/standard/commandline/snippets/define-commands/csharp/Program2.cs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ static async Task DefineArguments(string[] args)
2727
rootCommand.Add(delayArgument);
2828
rootCommand.Add(messageArgument);
2929

30-
rootCommand.SetHandler((int delayArgumentValue, string messageArgumentValue) =>
31-
{
32-
Console.WriteLine($"<delay> argument = {delayArgumentValue}");
33-
Console.WriteLine($"<message> argument = {messageArgumentValue}");
34-
},
35-
delayArgument, messageArgument);
30+
rootCommand.SetHandler((delayArgumentValue, messageArgumentValue) =>
31+
{
32+
Console.WriteLine($"<delay> argument = {delayArgumentValue}");
33+
Console.WriteLine($"<message> argument = {messageArgumentValue}");
34+
},
35+
delayArgument, messageArgument);
3636

3737
await rootCommand.InvokeAsync(args);
3838
// </definearguments>
@@ -51,9 +51,9 @@ static async Task DefineCommands(string[] args)
5151
// </definecommands>
5252

5353
sub1aCommand.SetHandler(() =>
54-
{
55-
Console.WriteLine(sub1aCommand.Description);
56-
});
54+
{
55+
Console.WriteLine(sub1aCommand.Description);
56+
});
5757

5858
await rootCommand.InvokeAsync(args);
5959
}
@@ -72,12 +72,12 @@ static async Task DefineOptions(string[] args)
7272
rootCommand.Add(delayOption);
7373
rootCommand.Add(messageOption);
7474

75-
rootCommand.SetHandler((int delayOptionValue, string messageOptionValue) =>
76-
{
77-
Console.WriteLine($"--delay = {delayOptionValue}");
78-
Console.WriteLine($"--message = {messageOptionValue}");
79-
},
80-
delayOption, messageOption);
75+
rootCommand.SetHandler((delayOptionValue, messageOptionValue) =>
76+
{
77+
Console.WriteLine($"--delay = {delayOptionValue}");
78+
Console.WriteLine($"--message = {messageOptionValue}");
79+
},
80+
delayOption, messageOption);
8181
// </defineoptions>
8282

8383
await rootCommand.InvokeAsync(args);
@@ -103,11 +103,11 @@ static async Task GlobalOption(string[] args)
103103
var subCommand1a = new Command("sub1a", "Second level subcommand");
104104
subCommand1.Add(subCommand1a);
105105

106-
subCommand1a.SetHandler((int delayOptionValue) =>
107-
{
108-
Console.WriteLine($"--delay = {delayOptionValue}");
109-
},
110-
delayOption);
106+
subCommand1a.SetHandler((delayOptionValue) =>
107+
{
108+
Console.WriteLine($"--delay = {delayOptionValue}");
109+
},
110+
delayOption);
111111

112112
await rootCommand.InvokeAsync(args);
113113
// </defineglobal>
@@ -125,12 +125,12 @@ static async Task RequiredOption(string[] args)
125125
var command = new RootCommand();
126126
command.Add(endpointOption);
127127

128-
command.SetHandler(
129-
(Uri? uri) =>
128+
command.SetHandler((uri) =>
130129
{
131130
Console.WriteLine(uri?.GetType());
132131
Console.WriteLine(uri?.ToString());
133-
}, endpointOption);
132+
},
133+
endpointOption);
134134

135135
await command.InvokeAsync(args);
136136
// </requiredoption>
@@ -145,12 +145,12 @@ static async Task HiddenOption(string[] args)
145145
var command = new RootCommand();
146146
command.Add(endpointOption);
147147

148-
command.SetHandler(
149-
(Uri? uri) =>
148+
command.SetHandler((uri) =>
150149
{
151150
Console.WriteLine(uri?.GetType());
152151
Console.WriteLine(uri?.ToString());
153-
}, endpointOption);
152+
},
153+
endpointOption);
154154

155155
await command.InvokeAsync(args);
156156
// </hiddenoption>
@@ -177,11 +177,11 @@ static async Task FromAmong(string[] args)
177177
var rootCommand = new RootCommand("Static list example");
178178
rootCommand.Add(languageOption);
179179

180-
rootCommand.SetHandler((string languageOptionValue) =>
181-
{
182-
Console.WriteLine($"--language = {languageOptionValue}");
183-
},
184-
languageOption);
180+
rootCommand.SetHandler((languageOptionValue) =>
181+
{
182+
Console.WriteLine($"--language = {languageOptionValue}");
183+
},
184+
languageOption);
185185

186186
await rootCommand.InvokeAsync(args);
187187
Console.WriteLine("Request help, provide a valid language, provide an invalid language.");

docs/standard/commandline/snippets/define-commands/csharp/scl.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="System.CommandLine" Version="2.0.0-beta3.22114.1" />
12+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
1313
</ItemGroup>
1414

1515
</Project>

docs/standard/commandline/snippets/dependency-injection/csharp/Program.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ static async Task Main(string[] args)
1515
rootCommand.Add(fileOption);
1616

1717
// <sethandler>
18-
rootCommand.SetHandler(async (FileInfo fileOptionValue, ILogger logger) =>
19-
{
20-
await DoRootCommand(fileOptionValue, logger);
21-
},
22-
fileOption, new MyCustomBinder());
18+
rootCommand.SetHandler(async (fileOptionValue, logger) =>
19+
{
20+
await DoRootCommand(fileOptionValue!, logger);
21+
},
22+
fileOption, new MyCustomBinder());
2323
// </sethandler>
2424

2525
await rootCommand.InvokeAsync("--file scl.runtimeconfig.json");
@@ -40,7 +40,7 @@ protected override ILogger GetBoundValue(
4040

4141
ILogger GetLogger(BindingContext bindingContext)
4242
{
43-
var loggerFactory = LoggerFactory.Create(
43+
ILoggerFactory loggerFactory = LoggerFactory.Create(
4444
builder => builder.AddConsole());
4545
ILogger logger = loggerFactory.CreateLogger("LoggerCategory");
4646
return logger;

docs/standard/commandline/snippets/dependency-injection/csharp/scl.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<ItemGroup>
1111
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
1212
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
13-
<PackageReference Include="System.CommandLine" Version="2.0.0-beta3.22114.1" />
13+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
1414
</ItemGroup>
1515

1616
</Project>

0 commit comments

Comments
 (0)