This works:
subcommand.Handler = CommandHandler.Create((int p1, int p2, int p3, ... int p7) => ...
This doesn't:
subcommand.Handler = CommandHandler.Create((int p1, int p2, int p3, ... int p8) => ...
Is it reasonable to ask to keep going a few more parameters since Func<> and Action<> support more?
My personal opinion is the CommandHandler.Create should probably include overloads to match the Func<> and Action<> arity.
Another approach that's worth considering: You can do bind complex objects.
command.Handler = CommandHandler.Create<MyType>(obj => /* */ );
Constructor injection and property setters are both supported, with the same naming conventions as we use to match parameters.
Can you say a bit more about this? I have a command with 9 options ... what should I do?
Let's assume you have a command line with options -a through -i. You can create a handler like this:
command.Handler = CommandHandler.Create<MyType>(myType => /* */ );
where MyType looks like either of the following:
public class MyType
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public string D { get; set; }
public string E { get; set; }
public string F { get; set; }
public string G { get; set; }
public string H { get; set; }
public string I { get; set; }
}
// or...
public class MyType
{
public MyType(string a, string b, string c, string d, string e, string f, string g, string h, string i)
{
A = a;
B = b;
C = c;
D = d;
E = e;
F = f;
G = g;
H = h;
I = i;
}
public string A { get; }
public string B { get; }
public string C { get; }
public string D { get; }
public string E { get; }
public string F { get; }
public string G { get; }
public string H { get; }
public string I { get; }
}
Thanks, that works nicely.
My personal opinion is the
CommandHandler.Createshould probably include overloads to match theFunc<>andAction<>arity.
I agree.
Aka, 16.
https://docs.microsoft.com/en-us/dotnet/api/system.func-17?view=netcore-3.1
Let's assume you have a command line with options
-athrough-i. You can create a handler like this:command.Handler = CommandHandler.Create<MyType>(myType => /* */ );where
MyTypelooks like either of the following:public class MyType { public string A { get; set; } public string B { get; set; } public string C { get; set; } public string D { get; set; } public string E { get; set; } public string F { get; set; } public string G { get; set; } public string H { get; set; } public string I { get; set; } } // or... public class MyType { public MyType(string a, string b, string c, string d, string e, string f, string g, string h, string i) { A = a; B = b; C = c; D = d; E = e; F = f; G = g; H = h; I = i; } public string A { get; } public string B { get; } public string C { get; } public string D { get; } public string E { get; } public string F { get; } public string G { get; } public string H { get; } public string I { get; } }
Can you show the
my.exe whatIsHere
for this?
Thanks.
This would work for the example:
> my.exe -a -b -c -d -e -f -g -h -i
Name matching is by parameter name for simple types, and by constructor or property names for more complex types.
I'm confused between what is the "flag", and what is the value.
Can you repost with values like
valuea valueb (etc, etc)
is the below valid for using "MyType" ?
my.exe -a valuea -b valueb -c valuec -d valued -e valuee -f valuef -g valueg -h valueh -i valuei
Sorry, what I posted would be valid for bools. Your example is correct.
I appreciate your quick responses.
I am currently getting:
Unrecognized command or argument 'valuea'
Unrecognized command or argument '-b'
Unrecognized command or argument 'valueb'
Unrecognized command or argument '-c'
Unrecognized command or argument 'valuec'
Unrecognized command or argument '-d'
Unrecognized command or argument 'valued'
Unrecognized command or argument '-e'
Unrecognized command or argument 'valuee'
Unrecognized command or argument '-f'
Unrecognized command or argument 'valuef'
Unrecognized command or argument '-g'
Unrecognized command or argument 'valueg'
Unrecognized command or argument '-h'
Unrecognized command or argument 'valueh'
Unrecognized command or argument '-i'
Unrecognized command or argument 'valuei'
mycommandone:
mycommandone_description
Usage:
MyExe mycommandone [options]
Options:
--mytypeone
==============================
The "args" that I am passing in
myexe.exe mycommandone --mytypeone -a valuea -b valueb -c valuec -d valued -e valuee -f valuef -g valueg -h valueh -i valuei
The code for setup:
public Command CreateCommand()
{
var cmd = new Command("mycommandone", "mycommandone_description");
cmd.AddOption(
new Option("--mytypeone", "mytypeone_description")
{
Argument = new Argument<MyType>() { Arity = ArgumentArity.ExactlyOne },
Required = true
});
cmd.Handler = CommandHandler.Create<MyType>((MyType myTypeInstance) =>
{
try
{
Console.WriteLine("the value of A is '{0}'", myTypeInstance.A);
Console.WriteLine("the value of B is '{0}'", myTypeInstance.B);
Console.WriteLine("the value of C is '{0}'", myTypeInstance.C);
Console.WriteLine("the value of D is '{0}'", myTypeInstance.D);
Console.WriteLine("the value of E is '{0}'", myTypeInstance.E);
Console.WriteLine("the value of F is '{0}'", myTypeInstance.F);
Console.WriteLine("the value of G is '{0}'", myTypeInstance.G);
Console.WriteLine("the value of H is '{0}'", myTypeInstance.H);
Console.WriteLine("the value of I is '{0}'", myTypeInstance.I);
return 0;
}
catch (Exception ex)
{
this.logger.LogError(ex, ex.Message);
return 999; /* ExceptionExitCode; */
}
});
return cmd;
}
I tried this as well:
MyExe.exe mycommandone mytypeone -a valuea -b valueb -c valuec -d valued -e valuee -f valuef -g valueg -h valueh -i valuei
===========
public Command CreateCommand()
{
var cmd = new Command("mycommandone", "mycommandone_description");
cmd.AddCommand(new Command("--mytypeone", "mytypeone_description"));
cmd.Handler = CommandHandler.Create<MyType>((MyType myTypeInstance) =>
{
try
{
Console.WriteLine("the value of A is '{0}'", myTypeInstance.A);
Console.WriteLine("the value of B is '{0}'", myTypeInstance.B);
Console.WriteLine("the value of C is '{0}'", myTypeInstance.C);
Console.WriteLine("the value of D is '{0}'", myTypeInstance.D);
Console.WriteLine("the value of E is '{0}'", myTypeInstance.E);
Console.WriteLine("the value of F is '{0}'", myTypeInstance.F);
Console.WriteLine("the value of G is '{0}'", myTypeInstance.G);
Console.WriteLine("the value of H is '{0}'", myTypeInstance.H);
Console.WriteLine("the value of I is '{0}'", myTypeInstance.I);
return 0;
}
catch (Exception ex)
{
string extraMsg = string.Format(ErrorMessageSwallowingExceptionAndReturningCode, ExceptionExitCode);
this.logger.Log(new LogEntry(LoggingEventTypeEnum.Error, extraMsg, ex));
return ExceptionExitCode;
}
});
return cmd;
}
=================================
==============================================
'mytypeone' was not matched. Did you mean 'mytypeone'? << may want to look into this separate from my issue(s) :)
Unrecognized command or argument 'mytypeone'
Unrecognized command or argument '-a'
Unrecognized command or argument 'valuea'
Unrecognized command or argument '-b'
Unrecognized command or argument 'valueb'
Unrecognized command or argument '-c'
Unrecognized command or argument 'valuec'
Unrecognized command or argument '-d'
Unrecognized command or argument 'valued'
Unrecognized command or argument '-e'
Unrecognized command or argument 'valuee'
Unrecognized command or argument '-f'
Unrecognized command or argument 'valuef'
Unrecognized command or argument '-g'
Unrecognized command or argument 'valueg'
Unrecognized command or argument '-h'
Unrecognized command or argument 'valueh'
Unrecognized command or argument '-i'
Unrecognized command or argument 'valuei'
mycommandone:
mycommandone_description
Usage:
Surescripts.Direct.Provisioning.AdminCommandLineInterface mycommandone [command]
Commands:
--mytypeone mytypeone_description
When binding to a complex type you still need to define the options. Here's a full working example:

Note that I changed the invoked command to RootCommand and attached the handler to the inner command ("mytypeone").
Also note that if you use the MyType variant that has constructor parameters and no setters, then the incomplete command line will result in null being passed to the handler, since there was no way to pass all of the required arguments. Making the constructor arguments optional will get you an instance, though.
I appreciate your help.
I am still getting issues.
I've coded up a clean-sample here:
https://github.com/granadacoder/system-command-line-demo/tree/feature/InitialCommit
If you get a chance to look, I would be grateful. Thank you.
README has enough notes to get going.
I coded it to your example...."MyTypeCommandCreator.cs".
But still getting:
Required command was not provided.
Unrecognized command or argument '--mytypeone'
Unrecognized command or argument '-a'
Unrecognized command or argument 'valuea'
Unrecognized command or argument '-b'
Unrecognized command or argument 'valueb'
Unrecognized command or argument '-c'
Unrecognized command or argument 'valuec'
Unrecognized command or argument '-d'
Unrecognized command or argument 'valued'
Unrecognized command or argument '-e'
Unrecognized command or argument 'valuee'
Unrecognized command or argument '-f'
Unrecognized command or argument 'valuef'
Unrecognized command or argument '-g'
Unrecognized command or argument 'valueg'
Unrecognized command or argument '-h'
Unrecognized command or argument 'valueh'
Unrecognized command or argument '-i'
Unrecognized command or argument 'valuei'
Ok,
My RunJustRootCommandDemo does work... if I send this in. (note the no prefix "--" on the first argument)
mytypeone -a valuea -b valueb -c valuec -d valued -e valuee -f valuef -g valueg -h valueh -i valuei
But check out my "RunParserDemo" method.
Basically, if the RootCommand gets pushed to a Parser, the code fails. This may be intentional..just highlighting it.
CommandLineBuilder clb = new CommandLineBuilder();
RootCommand cmd = /* from MyTypeCommandCreator */
this.logger.LogInformation(string.Format(LogMessageAddingICommandCreator, cmd.Description));
clb.AddCommand(cmd);
Parser prsr = clb
.UseDefaults()
.Build();
return prsr;
All of this code in the github repo above.
I see that you're using an old version, System.CommandLine.Experimental. Give it a try with the latest here: https://www.nuget.org/packages/System.CommandLine/.
This probably is not related to any issues you've described, though.
Gaaaaaaaaaaaa! I updated the nuget package and pushed the code. ( I did not realize I was using an experimental outdated version..Thanks.)
https://www.nuget.org/packages/System.CommandLine/
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20253.1" />
Short Version:
This is WORKING !!
mytypeone -a valuea -b valueb -c valuec -d valued -e valuee -f valuef -g valueg -h valueh -i valuei
Not working is adding it to a "Parser". In case you're interested.
Working Output:
parameter count = 19
Arg[0] = [mytypeone]
Arg[1] = [-a]
Arg[2] = [valuea]
Arg[3] = [-b]
Arg[4] = [valueb]
Arg[5] = [-c]
Arg[6] = [valuec]
Arg[7] = [-d]
Arg[8] = [valued]
Arg[9] = [-e]
Arg[10] = [valuee]
Arg[11] = [-f]
Arg[12] = [valuef]
Arg[13] = [-g]
Arg[14] = [valueg]
Arg[15] = [-h]
Arg[16] = [valueh]
Arg[17] = [-i]
Arg[18] = [valuei]
cmd.GetType='RootCommand'
the value of A is 'valuea'
the value of B is 'valueb'
the value of C is 'valuec'
the value of D is 'valued'
the value of E is 'valuee'
the value of F is 'valuef'
the value of G is 'valueg'
the value of H is 'valueh'
the value of I is 'valuei'
RootCommand.InvokeAsync='0'
The root command isn't intended to be supplied on the command line, though the parser does understand it so that you can, for example, parse a complete, un-split string.
So, for example, for input like this:
> my.exe subcommand1 --option1 argument
Main will get called with a string[] args of new[] { "subcommand1", "--option1", "argument" }. Arg zero, i.e. "my.exe", is not passed to Main.
But System.CommandLine lets you do any of the following with equivalent results:
parser.Parse("subcommand1 --option1 argument");
parser.Parse("my.exe subcommand1 --option1 argument");
parser.Parse(new [] { "subcommand1", "--option1", "argument" } );
parser.Parse(new [] { "my.exe","subcommand1","--option1","argument" } );
I finally figured it out. I am able to have multiple Commands off the (vanilla) RootCommand now. I was (errantly) using the Parser when I should have been using the RootCommand. I'll update my sample when I get a chance.
Thanks for your quick feedback.
Ok, I've updated the sample.
https://github.com/granadacoder/system-command-line-demo
This shows strong binding to a simple POCO (two different Poco's)....with 2 "forks" for the functionality.
Basically, this:
example one
MyCompany.MyExamples.SystemCommandLineOne.ConsoleOne.exe mytypeone -a valuea -b valueb -c valuec -d valued -e valuee -f valuef -g valueg -h valueh -i valuei
example two
MyCompany.MyExamples.SystemCommandLineOne.ConsoleOne.exe showdatetime --includedate false --dateformat "MM/dd/yyyy" --includetime
I'm "good" now. Thanks for the help.
You're welcome!
Most helpful comment
Another approach that's worth considering: You can do bind complex objects.
Constructor injection and property setters are both supported, with the same naming conventions as we use to match parameters.