NET Core CLI - Create/update/modify/build and package a new project/solution

NET Core CLI - Create/update/modify/build and package a new project/solution

In today's article we are going to continue our exploration on .NET core concepts. As we already mentioned in our previous article, NET Core comes with a cross platform CLI, which is a command-line interface (CLI) tool used for developing, building, running, and publishing .NET Core applications. The .NET Core CLI is included with the .NET Core SDK. Installing only dotnet core runtime will not bring this tooling support into your development machine. Also, it is worth mentioning that, multiple dotnet CLI toolset versions can be installed side by side on your machine.

One of the best parts of .NET was Visual Studio, an IDE that allowed you to easily write code with code completion, easy debugging and packaging of your apps. Years later, .NET Core came into play and with it, the ability to use .NET cross-platform and with a very capable CLI tool Using the old .NET you knew there was an msbuild command in the background, but you seldom needed to care, Visual Studio handled it all for you. Using .NET Core though you have the best of both worlds, still the first-class experience of either Visual Studio or Visual Studio Code and with the CLI tool we have a first-rate terminal experience as well.

No alt text provided for this image

Now you might ask, why even bother using terminal commands, when I have pretty powerful IDEs that do all the job for me? Some of the strongest reasons, would include the following:

  • Scripting: Allows us to easily setup CI / CD pipelines for our applications
  • Speed: Working with the terminal is usually faster than using a visual environment
  • Choose your own editor: Allows us to use the IDE of our choice. Also .NET used to be very tightly coupled to Visual Studio. Now with .NET Core CLI, we can quite easily build, test, package and publish our projects through the terminal
  • Finally, the most important reason in my opinion is that it is always good to know what commands run under the hood and get some insights on what they actually do for you

Command Structure

A .NET Core CLI command consists of the following components:

  • The driver ("dotnet")
  • The command ([verb])
  • Possibly command arguments and finally
  • Possible options

No alt text provided for this image

You see this pattern in most CLI operations, such as creating a new console app and running it from the command line, as the following commands show when executed from a directory named my_app.

No alt text provided for this image

Driver

Let's drill down on the components of a CLI command one by one. The driver, as we already saw, is named dotnet and has two responsibilities:

  • Running a framework-dependent app or?
  • Executing a command

To be able to run a framework-dependent app, we can specify the app after the driver, for example something like the following command: dotnet /path/to/my_app.dll. If we wanted to use a specific version of the .NET Core Runtime, we can also use the --fx-version <VERSION> option.

When we supply a command to the driver, dotnet.exe starts the CLI command execution process (e.g. dotnet build). First, the driver determines the version of the SDK to use. If there is no global.json file, the latest version of the SDK available is used and it might be either a preview or a stable version, depending on what is latest on the machine. Finally, once the SDK version is determined, it executes the command.

The global.json file allows us to define which .NET Core SDK version is used when we run .NET Core CLI commands. Selecting the .NET Core SDK is independent from specifying the runtime our project targets. As a rule of thumb, we usually want to use the latest version of the SDK tools, so no global.json file is needed. NET Core SDK looks for a global.json file in the current working directory or one of its parent directories.

Command

The command performs an action. For example:

  • dotnet build builds code and
  • dotnet publish publishes code

The commands are implemented as a console application using a dotnet {command} convention.

Arguments & Options

The arguments you pass on the command line are the arguments provided as input to the command invoked. For example, when we execute dotnet publish my_app.csproj, the my_app.csproj argument indicates the project to publish and is passed as input to the publish command.

The options you pass on the command line are the options also provided as input to the command invoked. For example, when we execute dotnet publish --output /build_output, the --output option and its value are passed as input to the publish command.

Let's begin exploring various NET Core CLI commands one by one.

dotnet new

Creates a new project, configuration file, or solution based on the specified template. The command calls the template engine to create the artifacts on disk based on the specified template and options.

An example syntax:

  • dotnet new <type of project template> -o <name of output directory>

No alt text provided for this image

dotnet restore

Restores the dependencies of a project. For example, suppose you have added NuGet packages to your solution. This automatically adds a reference to your project’s .csproj file. If you check out the project from GitHub those packages are not versioned and will need to be added to your project. Running restore at this point will fetch the packages from the NuGet repository.

The command can be run in two ways:

  • Explicitly, you type dotnet restore in the command line and this fetches the packages you need. The explicit command is particularly useful in certain scenarios, where explicit restoring makes sense, such as continuous integration builds in Azure DevOps Services or in build systems that need to explicitly control when the restore occurs.
  • Implicitly, where the dotnet restore runs as part of another command being executed. Here there is no need to type dotnet restore, it's run implicitly by all commands that require a restore to occur, such as: dotnet new, dotnet build, dotnet run, dotnet test, dotnet publish, and dotnet pack. To disable implicit restore, use the --no-restore option

dotnet build

Builds a project and its dependencies into a set of binaries. If you are at the root directory and you have a solution, then it will build the whole solution. If you are in a specific project directory, then it will only build that project. The binaries include the project's code in Intermediate Language (IL) files with a .dll extension. Depending on the project type, other files may be included, such as:

  • An executable that can be used to run the application, if the project type is an executable targeting .NET Core 3.0 or later
  • Symbol files used for debugging with a .pdb extension
  • A .deps.json file, which lists the dependencies of the application or library
  • A .runtimeconfig.json file, which specifies the shared runtime and version for the application
  • Other libraries that the project depends on (via project references or NuGet package references)

For executable projects targeting versions earlier than .NET Core 3.0, library dependencies from NuGet are typically NOT copied to the output folder. With that in mind, the product of dotnet build isn't ready to be transferred to another machine to run.

On the other hand, for executable projects targeting .NET Core 3.0 and later, library dependencies are copied to the output folder. This means that if there isn't any other publish-specific logic, the build output should be deployable.

dotnet run

Provides a convenient option to run your application from the source code with one command. The command depends on the dotnet build command to build the code. Output files are written into the default location, which is bin/<configuration>/<target>. For example if you have a netcoreapp2.1 application and you type dotnet run, the output is placed in bin/Debug/netcoreapp2.1. Files are overwritten as needed and temporary files are placed in the obj directory.

If the project targets multiple frameworks, executing dotnet run results in an error unless the -f | --framework <FRAMEWORK> option is used to specify the framework. The dotnet run command is used in the context of projects, not built assemblies. If you're trying to run a framework-dependent application DLL instead, you must use dotnet without a command. For example, to run myapp.dll, use?dotnet myapp.dll.

Keep in mind, that it's not recommended to use dotnet run to run applications in production. Instead, you should create a deployment using the dotnet publish command and deploy the published output.

dotnet test

Used to execute unit tests in a given solution. The dotnet test command builds the solution and runs a test host application for each test project in the solution. The test host executes tests in the given project using a test framework, for example: MSTest, NUnit, or xUnit and reports the success or failure of each test. If all tests are successful, the test runner returns 0 as an exit code; otherwise if any test fails, it returns 1. For multi-targeted projects, tests are run for each targeted framework.

Test projects specify the test runner using an ordinary <PackageReference> element, as seen in the sample project file below. Where you see "Microsoft.NET.Test.Sdk" is the test host, "xunit" is the test framework and "xunit.runner.visualstudio" is a test adapter, which allows the xUnit framework to work with the test host.

No alt text provided for this image

dotnet pack

Builds the project and creates NuGet packages. The result of this command is a NuGet package (that is, a .nupkg file). NuGet dependencies of the packed project are added to the .nuspec file, so they're properly resolved when the package is installed.

dotnet clean

This command cleans the output of the previous build. This means it takes away both the contents of obj as well as bin folders.

dotnet sln

This is the command you use to manage your solution. A solution keeps track of many projects and it’s a great way of managing a group of different projects that logically belongs together to do things like building or maybe publish an executable.

To create a solution you just place yourself in a directory of your choosing and run the dotnet new sln command. For example running the below commands:

  1. cd hello
  2. dotnet new sln

will create a hello.sln file inside a folder named hello.

To manage the solution you have the following commands at your disposal:

  • add, this will add a project to your solution
  • remove, this will remove a project from your solution
  • list, this lists all the projects in a solution

dotnet add/remove reference

The dotnet add reference command will add a reference from a project to another project. For example, you are currently developing the console project app and you want to add a reference to the project lib to app. You can do this in one of two ways:

  • Add a reference from solution dir, if you are standing in the solution directory (one level above the app directory) then you type: dotnet add app/app.csproj reference lib/lib.csproj
  • Add a reference from the current directory, then you would type: dotnet add reference lib/lib.csproj

The dotnet remove reference command removes the specified reference from our project respectively.

To list the references of your project you can use the dotnet list reference command.

dotnet add/remove package

The dotnet add package command allows us to add a package from NuGet by specifying it by name. For example, typing dotnet add package Newtonsoft.Json, will add Newtonsoft.Json package to our project. It will also run a dotnet restore command which will fetch the package from NuGet's repository.

The dotnet remove package command removes the specified package from our project respectively.

To list the packages of your project you can use the dotnet list package command.

dotnet publish

Publishes the application and its dependencies to a folder for deployment to a hosting system. It compiles the application, reads through its dependencies specified in the project file, and publishes the resulting set of files to a directory

The output includes the following assets:

  • Intermediate Language (IL) code in an assembly with a .dll extension
  • A .deps.json file that includes all of the dependencies of the project
  • A .runtimeconfig.json file that specifies the shared runtime that the application expects
  • The application's dependencies, which are copied from the NuGet cache into the output folder

The dotnet publish command's output is ready for deployment to a hosting system for execution. Depending on the type of deployment that the project specifies, the hosting system may or may not have the .NET Core shared runtime installed on it.

What does the above mean? Basically, there are 2 types of deployment:

  • FDD or Framework-Dependent Deployment: Only your app and other related packages & configurations are packed in the published package. The target machine to run your app needs to have the .NET Core runtime installed. Published package is platform-independent. FDD has options like

  1. Portable - Totally platform independent. Total binary size is small, comes with your own DLLs & some .NET Core base types
  2. Runtime specific – Comes also with bunch of runtime specific files. Binary size much larger than the portable one

  • SCD or Self-Contained Deployment: The required parts of .NET Core runtime and Kestrel web server is packed with your application code and you can directly deploy the package on a machine and run the app. In other words, the machine does not need the .NET Core runtime to be pre-installed. This is a platform specific deployment and that means you must choose the target runtime at the time of publish e.g. win-x64 or linux-x64. In .NET Core, you specify a target platform with a runtime ID (<RID>). A runtime ID is nothing but a combination of a specific OS and processor architecture e.g. win-x86, linux-x64 or osx-x64

No alt text provided for this image

Commands Summary

No alt text provided for this image

Demo

Ok enough with the theory, time to see a small demo. Let's follow some steps one by one and see the result of each NET Core CLI command we execute:

  • Create a new folder named “netcoreclidemo” inside “C:\temp” folder
  • Open a powershell window inside the new folder (Ctrl + Shift + Right Click inside the folder and choose the option "Open Powershell window here"
  • Run dotnet new console to create a new console project

No alt text provided for this image
No alt text provided for this image

  • Run dotnet build to build the project

No alt text provided for this image

  • Run dotnet run from inside the folder that the netcoreclidemo.csproj file resides to run the app

No alt text provided for this image

  • In Powershell, go to bin/Debug/{framework} folder and type code . to inspect the contents of the folder (or open it with a simple NotePad if you do not have Visual Studio Code installed in your PC)

No alt text provided for this image
No alt text provided for this image

  • Go back to “C:\temp\netcoreclidemo” folder and now run dotnet add package Newtonsoft.Json

No alt text provided for this image

  • Run dotnet list package

No alt text provided for this image

  • Run dotnet publish and inspect the publish folder

No alt text provided for this image
No alt text provided for this image

  • Run dotnet C:\temp\netcoreclidemo\bin\Debug\net5.0\publish\netcoreclidemo.dll

No alt text provided for this image

  • Run dotnet publish --runtime win-x64

No alt text provided for this image

  • Inspect again the win-x64 created folder content

No alt text provided for this image

  • Finally run the self-contained app again with dotnet .\bin\Debug\net5.0\win-x64\netcoreclidemo.dll

No alt text provided for this image

For anyone interested on playing with the dotnet core CLI commands, there is a lab exercise in this GitHub link. Just follow the instructions listed there and have fun!

That's all for today. Cheers!

要查看或添加评论,请登录

Orestis Meikopoulos的更多文章

社区洞察

其他会员也浏览了