Search code examples
c#.net-coresystem.commandline

Value passed to CommandHandler is null


I am trying to learn how to use System.CommandLine. I have created a simple code which need to accept a url and an optional parameter of file path.

    static async Task<int> Main(string[] args)
    {
        var cmd = new RootCommand("Save URL content to file");
        cmd.AddArgument(new Argument<string>("URL", "The network resource url you want to retrieve"));
        cmd.AddOption(new Option<FileInfo>(new[] { "--output-document", "-O" }, "write document to FILE"));
        cmd.Handler = CommandHandler.Create<string, FileInfo, IConsole >(HandleDownload);        

        return await cmd.InvokeAsync(args);
    }

    static void HandleDownload(string url, FileInfo file, IConsole console)
    {
        console.WriteLine(url);
        console.WriteLine(file.FullName);
    }

When I ran with below arguments

"http://www.google.com" --output-document c:\test.html

Inside HandleDownload url variable is gets assigned correctly but the 'file' variable is appearing as null. What am I doing wrong?


Solution

  • It seems you are using an older version of System.CommandLine, or a newer version with the additional "NamingConventionBinding" package.

    In either of these cases, the command handler parameters will be associated with an option/argument by matching the handler parameter name with option/argument aliases.

    And here you tripped up.[1]

    Your FileInfo option has the aliases --output-document and -O. But your command handler has no parameter with a matching name (which should be something like FileInfo outputDocument or FileInfo o).

    Your command handler has a parameter named file, however. But there is no option or argument with an alias that would correlate with the name "file". Hence, System.CommandLine is passing null for this parameter.

    So, to fix your issue it should be sufficient to just rename the file parameter of the handler to outputDocument, and Bob should be your uncle...



    [1] By the way, you are not the only one who tripped up with this. There were a number of reports in the issue tracker of System.CommandLine's github repo with users tripping up in pretty much the same fashion -- using mismatching handler parameter names that led to unexpected behavior that was not intuitively understood. (And then there is also the pain of having to refactor handler parameter names, if one were to rename an option alias for a better, neater alias. Sigh...) Among other reasons, that eventually led the author(s) of the package to not use naming convention binding as default anymore for newer versions of System.CommandLine. Establishing a handler now requires explicit association of each handler parameter with the respective Option/Argument instance. (If it is desired to keep using naming convention binding with newer System.CommandLine versions, the additional package System.CommandLine.NamingConventionBinding will enable this.)