Recently, I was working with a developer who was putting the final touches on a command-line tool. I usually opt for a good Web-App and API, but it got me thinking about a pair of tools I've used to package up Windows console applications for use. ManyConsole makes command-line parameter creation and documentation easy and Fody abstracts a lot of MSBuild configuration to pack required dll files into one *.exe. Both tickle my fancy in the "get stuff done quickly and well" buttons. I'll cover Fody in the next post.
ManyConsole
Install-Package ManyConsole
Extending NDesk.Options, ManyConsole aims to speed up argument parsing in a manner that keeps the main method uncluttered.
ManyConsole provides a console interface for the user to list available commands, call and get help for each. - ManyConsole Quickstart Guide
Let's assume we want a command to run a ThingyService which will ostensibly print some results to some csv file.
- We want to give the caller the option to override the default file name
- We want to give the caller the option to add a timestamp to the filename
The first thing we'll do is build the command:
    public class DoTheThingCommand : ConsoleCommand
    {
        // using dependency injection to resolve
        private readonly IThingyService _thingy;
        // set a default filename for some output
        private string Filename { get; set; } = "ThingyReport.csv";
        // build the command
        public DoTheThingCommand(IThingyService thingy)
        {
            _thingy = thingy;
            // define command line switch and description
            IsCommand("csv", "output csv report");
            HasLongDescription("outputs a CSV report for the thingy to the filesystem");
            // build options
            HasOption("f|filename:", "specify filename",
                f => Filename = f ?? Filename);
            HasOption("t|timestamp:", "add timestamp to filename",
                t => Filename = $"{Filename}-{DateTimeOffset.Now:yy-MM-dd}");
        }
        // define the behavior
        public override int Run(string[] remainingArguments)
        {
            _thingy.DoIt(Filename);
            return 1;
        }
    }
We'll add a little Dependency Injection:
    public static class Container
    {
        public static IContainer Build()
        {
            var builder = new ContainerBuilder();
            // register the service implementation
            builder.RegisterType<ThingyService>().As<IThingyService>();
            // register all ManyConsole.ConsoleCommands
            builder.RegisterAssemblyTypes(typeof(Program).Assembly)
                .Where(x => x.IsSubclassOf(typeof(ConsoleCommand)))
                .As<ConsoleCommand>();
            return builder.Build();
        }
    }
Sample Main, resolving the implemented console commands
    public class Program
    {
        public static int Main(string[] args)
        {
            using (var scope = Container.Build().BeginLifetimeScope())
            {
                return ConsoleCommandDispatcher
                    .DispatchCommand(
                        scope.Resolve<IEnumerable<ConsoleCommand>>(), 
                        args, 
                        Console.Out);
            }
        }
    }
All the options for the command-line argument have been placed in the same area, while still allowing the dependency injector to handle the details of the service itself.
Now here's the payoff: when I run the console command someprogram.exe /?, I'll get the following output:
Extra parameters specified: /?
'csv' - output csv report
outputs a CSV report for the thingy to the filesystem
Expected usage: ManyConsoleReference.exe csv <options>
<options> available:
  -f, --filename[=VALUE]     specify filename
  -t, --timestamp[=VALUE]    add timestamp to filename
This shows me I can run the command someprogram.exe csv -f "Spiffy.csv"-t which will change the filename and add a timestamp.
I can also add many other commands all segmented from each other with the documentation built into the command definitions.
Software Development Nerd
