Friday, January 27, 2017

Quick post about Command Line Arguments in C# Console Applications

As you might can tell by the context switch to the PnP.Core series, I am building an install utility for SharePoint for my current client.  One of the things that I always try to do is provide artifacts to clients that are easy to use and flexible.  The way that I accomplish that with command line tools is to utilize the Command Line Parser Library to handle any inputs necessary to choose what the application is doing.  I also use an XML configuration file which I read using a PowerShell script which builds the argument list for the exe.  My goal is to make it a repeatable process.  What I realized today when the requirements of the tool changed (shocker, I know) while doing a mock deployment with their engineering team, is that it also makes it extremely easy to maintain and modify.

To get into some non-specific specifics, I had been told that I needed to use one type of authentication to connect to their SharePoint servers and it worked great in the QA environment but was failing in UAT.  I use a completely different authentication method within our Dev environment, so I wasn't really shocked, but we had 8 people on the call so turning the change around quickly was important. All that I had to do is make the required authentication change, add another command line argument, add a line to the configuration file, and update the PowerShell script to pass it in.  It took me less than 10 minutes to update both deployment programs (obey Curly's Law!) and provide bundles to the installation team.  Everyone was a little shocked because they expected us to have to pick the test up again on Monday to give me time to make the necessary changes.

As an aside, this isn't uncommon in large enterprises.  They often suffer from a bad case of Snowflake Servers and that leads to friction during the deployment process because each environment you move into is a new challenge.  Separation of Concerns also keeps you from being able to do your own deployment into at least UAT and Prod, but if you plan ahead and know that there are going to be environmental differences you can engineer the required flexibility into your tools.

Anyway, back to the point.  It is easy to add your command line argument parsing using the library.  You build an Options object with decorated properties and pass an instance of it and the args array to a static method and you are done.  Here is a simplified framework for your Main method:

    static void Main(string[] args)
    {
        var options = new Options();
        if (CommandLine.Parser.Default.ParseArguments(args, options))
        {
            // do your stuff here to validate the parameters
            // and to setup how the program runs
        }
        if (options.ShowingHelp)
        {
            return;
        }
        // do the actual work

    }
\Here is a sample Options object:

    class Options
    {
        [Option('v', "verbose", DefaultValue = true,
          HelpText = "Prints all messages to standard output.")]
        public bool Verbose { get; set; }

        [Option('u', "userName", Required = false,
          HelpText = "Username for a user with rights to create "+
            "lists on the destination site.")]
        public string UserName { get; set; }

        [Option('p', "password", Required = false,
          HelpText = "Password for a user with rights to create "
            +"lists on the destination site.")]
        public string Password { get; set; }

        [ParserState]
        public IParserState LastParserState { get; set; }

        [HelpOption]
        public string GetUsage()
        {
            ShowingHelp = true;
            return HelpText.AutoBuild(this,
              (HelpText current) =>
              HelpText.DefaultParsingErrorsHandler(this, current));
        }

        public bool ShowingHelp { get; set; }

    }

Getting started is as easy as:
  1.  Create your console application
  2.  Install the NuGet package
Command Line Parser NuGet Package

  3.  Add an Options object with decorated properties
  4.  Call the parser in your Main method
  5.  Use the results

Within the section for reacting to the options, I generally add lines like this to give proactive feedback to the person using the program:

    if (options.Verbose && !string.IsNullOrEmpty(options.UserName))
    {
        Console.WriteLine("User Name: {0}", options.UserName);
    }
    if (options.Verbose && !string.IsNullOrEmpty(options.Password))
    {
        Console.WriteLine("Password (obscured): {0}", ".........");

    }

I hope this helps!  Keep your code clean, see you tomorrow.

No comments:

Post a Comment