TECH Insights

Viden og indsigter fra IT Minds' it-talenter

Creating a single-file executable CLI with ASP.NET Core 3.0

Skrevet af Mindster og Software Developer hos IT Minds on 17-10-19 12:55
Mindster og Software Developer hos IT Minds

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.

Working with System.CommandLine

When working with System.CommandLine there are multiple approaches

DragonFruit

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

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

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.

Show me the code

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:

Screenshot 2019-10-17 at 12.38.19

To get started with the CLI project run the following command to scaffold you Console Application

Screenshot 2019-10-17 at 12.39.11

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)

Screenshot 2019-10-17 at 12.39.49

To make it console friendly a wrapper class is implemented to handle the console output.

Screenshot 2019-10-17 at 12.40.38

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.

Screenshot 2019-10-17 at 12.43.17

Screenshot 2019-10-17 at 12.43.35

Screenshot 2019-10-17 at 12.43.55

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:

Screenshot 2019-10-17 at 12.45.48

After that please go into the destination folder of the build. If your setup is similar to mine it'll be:

Screenshot 2019-10-17 at 12.46.29

Now you can test the application by running the following:

Screenshot 2019-10-17 at 12.47.14

Which produces the following output:
Screenshot 2019-10-17 at 12.48.01

Let's do a simple test by adding two doubles, you can do this by running the following:

Screenshot 2019-10-17 at 12.49.03

Which gives the output: 

Screenshot 2019-10-17 at 12.49.46

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:

Screenshot 2019-10-17 at 12.50.27

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:

Screenshot 2019-10-17 at 12.51.34

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.

A closer look at trimming

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:

Screenshot 2019-10-17 at 12.52.17

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!

Conclusion

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.

Insights fra de bedste softwaretalenter

En del af IT Minds’ DNA er, at vi spotter og tiltrækker de bedste softwaretalenter, som er stærke i de nyeste teknologier og frameworks. Derfor vil vi gerne dele ud af vores viden og erfaringer vores konsulenter opsamler, når de afprøver og arbejder med nye teknologier og frameworks.

Seneste indlæg