A new experimental feature in .NET Core is the System.CommandLine NuGet package(s). These make it possible for developers to create CLI (Command Line Interface) projects using the console application template from .NET Core.
This is interesting since it enables you to expose existing business logic class libraries via a CLI.
When working with System.CommandLine there are multiple approaches
One approach is to use an extra NuGet Package called System.CommandLine.DragonFruit, this package allows you to make strongly typed input arguments for your Main class, afterwards the package will generate appropriate commands, options and arguments for running the application as a CLI. This is however in my opinion not the best solution as it generates input arguments as options instead of Arguments forcing user to explicitly declare argument names.
Method first relies on reflection to find the appropriate method inside the Application and create an invocation for it. I'm not a fan of this solution and will use no further time explaining it. Please see the System.CommandLine wiki for more info.
Syntax first is a declarative approach where you define your commands, options and arguments in code. Furthermore the invocation of the given commands are also declared in code. This is the approach we will be using for this demonstration.
To emulate the scenario where you have existing Business Logic you want to expose through a CLI we have implemented the class Calculator. It is a very simple static calculator class containing four methods (add, subtract, multiply and divide). See the calculator code here:
To get started with the CLI project run the following command to scaffold you Console Application
Add the necessary NuGet packages with the following commands (Please note that as of writing this, these are experimental features which is why we include an alpha version)
To make it console friendly a wrapper class is implemented to handle the console output.
All this is all fine and well but, there's nothing new here. For the System.CommandLine magic to shine through we need to take a look at our Program.cs.
To see the full code snippet visit: https://dev.to/itminds/grpc-in-asp-net-core-3-0-pm
That's actually it. now we can build the project using the following:
After that please go into the destination folder of the build. If your setup is similar to mine it'll be:
Now you can test the application by running the following:
Which produces the following output:
Let's do a simple test by adding two doubles, you can do this by running the following:
Which gives the output:
Publish Single file
To enable a true cli experience we would like to ship our application as a single executable file.
This has been made possible in .NET Core 3.0 - please modify your .csproj file to look like the following:
The new properties will be described below:
PublishSingleFile: When true a single platform-specific single-file executable. This is the reason for setting <RuntimeIdentifier> property. See single-file bundler design document
PublishTrimmed: When true the compiler analyses the IL code and removes unused assemblies to optimize the size of the final artifact. Note! If your application uses Reflection or related dynamic features might break as the compiler cannot resolve the dynamic features. This can be prevented by introducing a list for the linker of DLL's it cannot remove. See the release announcement for .NET Core 3.0 or the documentation for the IL Linker.
PublishReadyToRun: This enables R2R (Ready To Run) compilation which is a kind of AOT (Ahead OF Time) compilation. This introduces restrictions in regard to cross-platform compilation and architecture. Please see the release announcement for .NET Core 3.0.
Now when you publish your project using:
You will get a single exe file that contains your application. It would seem that the first time you run this application it extracts the necessary dll's to the temporary file storage as to be able to use them.
Let's take a look at how effective the trimming actually is, first i set the property to false in my .csproj file and then run:
I get a single-file executable that hasn't been trimmed. The size of this file is 67,787KB or roughly 67.7MB.
Now lets set the property to true again and publish the file anew.
After publishing with the property set to true, the size of our executable is 36,713KB or roughly 36.7MB, so the trimming actually almost halved the size of our executable - neat!
In my opinion this is an awesome development enabling users to expose business logic via a CLI. I especially see it as being extremely powerful in a DevOps setup where you can build customs utilities to manage stuff like database seeding, migration and so on. It is still experimental though, the code might change a lot before it's released.
All source code from this post can be found on Github.