Skip to content

Binding

Denis Zykov edited this page Jan 10, 2024 · 6 revisions

Binding

Verbs binding

This library uses reflection to get a list of available verbs. Each method with a suitable signature can be called via the command line. Requirements for method are:

  • Have return type of int or Task<int>
  • Be non-generic method
  • Be non-special method
  • Have no NonVerbAttribute attribute on it

By default, name matching is case-insensitive.

For each command line invocation, the best matching method is selected so that method overloads are supported.

Options binding

Options are bound to the corresponding method parameters by name.

int Hello(string name)
{
    Console.WriteLine("Hello " + name + "!");
    return 0;
}
myapp HELLO --name Jake
#>Hello Jake!

By default, name matching is case-insensitive:

myapp hello --NAME Jake
#>Hello Jake!

Required options could be bound by position:

myapp HELLO Jake
#>Hello Jake!

To be more detailed, values for positional binding are taken from the values and, accordingly, "steal" the elements from this list.

List options

You can add array/list options to collect multiple values as shown below:

int Hello(string[] names)
{
    Console.WriteLine("Hello " + string.Join(", ", names) + "!");
    return 0;
}
myapp HELLO --names Mike Jake
#>Hello Mike, Jake!

Options accepting multiple values can be only named. Such options capture all values after until another option or the end of options -- is encountered.

Non required options

By default option require value and cause error if none provided. You can make any option non required by specifying default value:

int Print(int myOptionalParam = 100)
{
    Console.WriteLine("myOptionalParam=" + myOptionalParam);
    return 0;
}
myapp PRINT --myOptionalParam 200
#>myOptionalParam=200
myapp PRINT
#>myOptionalParam=100

Non required options cannot be bound by position:

myapp PRINT 200
#>myOptionalParam=100

Flag options

Option could be declared as "flag" if it has bool type in declaration:

int Flag(bool myFlag)
{
    Console.WriteLine(myFlag ? "Flag is set" : "Flag is not set");
    return 0;
}
myapp.exe FLAG --myFlag
#>Flag is set

myapp.exe FLAG --myFlag true
#>Flag is set

myapp.exe FLAG
#>Flag is not set

myapp.exe FLAG false
#>Flag is not set

You can get the number of times this flag has been set in command line with following code:

[Alias("v")]
int Flag(OptionCount verbosityLevel)
{
    Console.WriteLine("Verbosity level: " + verbosityLevel);
    return 0;
}

This also works with short aliases:

myapp.exe FLAG -vvvv
#>Verbosity level: 4

Values binding

Any values not captured by options and not stolen by a positional binding end up in the collection of values and can be retrieved with params parameter.

int MyMethod(params string[] values)
{
    Console.WriteLine("Values are: " + string.Join(", ", values));
    return 0;
}
myapp.exe MYMETHOD First Second Third
#>Values are: First, Second, Third

Service resolution and DI

If you require services from your IServiceProvider within an verb method, then you have two options:

  • Declare a parameter with the required type of service and apply [FromService] attribute to it.
int AddAccount([FromService] IAccountService accountService, string name /* ... */)
{
    // ...
}
  • Or use the constructor of the verb's class to pass services that will be resolved from the configured IServiceProvider.
class AccountsApi
{
    private readonly IAccountService accountService;

    public AccountsApi(IAccountService accountService) // your IServiceProvider should support constructor parameter injection
    {
        this.accountService = accountService;
    }

    int AddAccount(string name /* ... */)
    {
        // ...
    }
}

Cancellation

While running CommandLine monitors the attempt to stop or interrupt the application and provides access to this event through CancellationToken. You can inject this token via a parameter:

Task<int> LongTask(CancellationToken cancellation)
{
    // ...
}

Verb execution context

You can access the execution context of the verb by declaring a special parameter:

int MyAwareTask(VerbExecutionContext context)
{
    // context.VerbSetBuilder
    // context.Verb
    // context.Arguments
    // context.ServiceProvider
    // context.Configuration
    // context.Properties
}

Supported .NET types

The following types are automatically bound and do not require additional configuration:

  • Primitive types (int, byte, char ...)
  • BCL types (String, DateTime, Decimal)
  • Nullable types
  • Enum types, including flags
  • Types with TypeConverterAttribute (Point, Guid, Version, TimeSpan ...)
  • Types with Parse(string value) method (IpAddress, Guid ...)
  • Types with explicit/implicit conversion from string

If you need to provide custom type conversion, then specify your ITypeConversionProvider service when configuring CommandLine.