In this article, we will learn more about how to use the CLI to build and run .NET applications.

Whether taking our first steps in cross-platform development based on the .NET common language runtime (CLR), or brushing up on some basics to explore a different way to work, we are in the right place. For an overview of the basics of C# and .NET, be sure to check out our C# Back to Basics series and Intro to .NET and C#.

Let’s start!

Create the First Project

Before we can run the command line interface (CLI), we need to make sure we have all the required files. The CLI is included in the .NET SDK. For more installation information, refer to Install .NET SDK. To verify that we can run the CLI, we can open the command line terminal and run the dotnet command. If it works, we should see the output:

Usage: dotnet [options]
Usage: dotnet [path-to-application]

Options:
  -h|--help         Display help.
  --info            Display .NET information.
  --list-sdks       Display the installed SDKs.
  --list-runtimes   Display the installed runtimes.

path-to-application:
  The path to an application .dll file to execute.

Create the Project

Those who have used Visual Studio to develop C# applications may remember that it offers many file and project templates that save us significant time and effort when creating new projects and files. The same templates are available for .NET cross-platform development with the dotnet new command. The basic structure of the command is:

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

dotnet new <template> -o <output directory> -n <name>

If we omit the name argument it will default to using the last part of the output directory, which is usually a reasonable default. Let’s use this command to create a console application:

dotnet new console -o HelloWorld

This produces output like the following:

The template "Console App" was created successfully.

Processing post-creation actions...
Restoring C:\Workspace\CodeMaze\HelloWorld\HelloWorld.csproj:
  Determining projects to restore...
  Restored C:\Workspace\CodeMaze\HelloWorld\HelloWorld.csproj (in 68 ms).
Restore succeeded.

As we can see, it created a new folder HelloWorld and inside it a C# project named HelloWorld.csproj. If we look inside the folder we will find a familiar project structure.

For more details about the dotnet new command and its argument please refer to the dotnet new command documentation.

Explore the Templates

For information about the installed templates we use the list subcommand:

dotnet new list

Which produces an output similar to the following:

These templates matched your input:

Template Name                                 Short Name           Language    Tags
--------------------------------------------  -------------------  ----------  --------------------------------
ASP.NET Core Empty                            web                  [C#],F#     Web/Empty
Class Library                                 classlib             [C#],F#,VB  Common/Library
Console App                                   console              [C#],F#,VB  Common/Console
dotnet gitignore file                         gitignore                        Config
Dotnet local tool manifest file               tool-manifest                    Config
MSTest Test Project                           mstest               [C#],F#,VB  Test/MSTest
NuGet Config                                  nugetconfig                      Config
NUnit 3 Test Item                             nunit-test           [C#],F#,VB  Test/NUnit
NUnit 3 Test Project                          nunit                [C#],F#,VB  Test/NUnit
Protocol Buffer File                          proto                            Web/gRPC
Solution File                                 sln,solution                     Solution
Web Config                                    webconfig                        Config
xUnit Test Project                            xunit                [C#],F#,VB  Test/xUnit
... 
(output truncated)

We can see that even though C# is the default language, we can also use F# and VB (--language argument). This list can also be expanded by installing new templates with the install subcommand.

Using CLI to Run .NET Applications

Now that we have created the project, we can use the .NET CLI to run it. We will first position ourselves in the project folder:

cd HelloWorld

And then run the project:

dotnet run

We should see the output:

Hello, World!

If we inspect the project folder now, we will see that dotnet compiled the project into an executable file, and then executed it from the output directory.

Additional arguments allow us to specify a different architecture or configuration, or even pass arguments to the console application that we want to execute. For more details, refer to the dotnet run command documentation.

This command is intended for fast, iterative development. We can combine it with the watch command for hot reloads and faster iterations.

The run command needs the source files and NuGet package cache, which will be compiled and restored by the build command that is implicitly executed. That’s why this is not a recommended way to run applications in a production environment. Instead, we generally use the publish command to create a deployment.  More about that in a minute.

Using the CLI to Build .NET Applications

When we run the .NET application using CLI, the build command is executed implicitly. However, dotnet is also used for scripting custom release pipelines. In case we need the explicit build step, we can do it like this:

dotnet build

We can expect output similar to this:

MSBuild version 17.6.1+8ffc3fe3d for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  HelloWorld -> C:\Workspace\CodeMaze\HelloWorld\bin\Debug\net7.0\HelloWorld.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.30

In this example, we see the use of MSBuild, the output path for the project, the outcome, and the total time it took to build this project. Also, there were no packages to restore. We’ll fix that in the next section.

There are more options for building a project than we can cover in the scope of this article. For more details, see the dotnet build command documentation.

Add the Package Reference

Assuming that our project requires interaction with some other web resources and that we found a library to make this job easier. We can add a reference to this library with the following command:

C:\Workspace\CodeMaze\HelloWorld>dotnet add package Flurl

The output will look similar to this:

  Determining projects to restore...
  Writing C:\Users\LENOVO\AppData\Local\Temp\tmp2F17.tmp
info : X.509 certificate chain validation will use the default trust store selected by .NET for code signing.
info : X.509 certificate chain validation will use the default trust store selected by .NET for timestamping.
info : Adding PackageReference for package 'Flurl' into project 'C:\Workspace\CodeMaze\HelloWorld\HelloWorld.csproj'.
info :   GET https://api.nuget.org/v3/registration5-gz-semver2/flurl/index.json
info :   OK https://api.nuget.org/v3/registration5-gz-semver2/flurl/index.json 360ms
info : Restoring packages for C:\Workspace\CodeMaze\HelloWorld\HelloWorld.csproj...
info :   GET https://api.nuget.org/v3-flatcontainer/flurl/index.json
info :   OK https://api.nuget.org/v3-flatcontainer/flurl/index.json 242ms
info :   GET https://api.nuget.org/v3-flatcontainer/flurl/3.0.7/flurl.3.0.7.nupkg
info :   OK https://api.nuget.org/v3-flatcontainer/flurl/3.0.7/flurl.3.0.7.nupkg 53ms
info : Installed Flurl 3.0.7 from https://api.nuget.org/v3/index.json with content hash ...
info : Package 'Flurl' is compatible with all the specified frameworks in project 'C:\Workspace\...
info : PackageReference for package 'Flurl' version '3.0.7' added to file 'C:\Workspace\CodeMaze\...
info : Writing assets file to disk. Path: C:\Workspace\CodeMaze\HelloWorld\obj\project.assets.json
log  : Restored C:\Workspace\CodeMaze\HelloWorld\HelloWorld.csproj (in 642 ms).

As we can see, dotnet checked the version compatibility, added the reference to the .csproj file, and downloaded the required package files (in a slightly different order). We can inspect the project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Flurl" Version="3.0.7" />
  </ItemGroup>

</Project>

Note the highlighted line that references the Flurl library, along with a specific version.

If we inspect the project folder we won’t find Flurl.dll or Flurl.nupkg. The reason is that referenced packages will be downloaded to the package cache which, on a development computer, is located in the user folder under .nuget.

We can now use the features offered by the library that we added. Let’s edit the Program.cs file:

using Flurl;

Console.WriteLine("Hello, World!");
Console.WriteLine("https://api.site.com".AppendPathSegments("area", "class"));

If we now run the run command:

dotnet run

We will see the output:

Hello, World!
https://api.site.com/area/class

As expected, the Flurl library correctly appended two path segments to the base URL.

Publish the Project

We have already covered how to build and run .NET applications using CLI. However, whether we are scripting the continuous integration/deployment (CI/CD) pipeline or we want to publish the project manually, we will need to make sure that we package all the required artifacts so that our project can execute properly in the target environment

Let’s run the publish command:

dotnet publish

The output shows us the most important information:

MSBuild version 17.6.1+8ffc3fe3d for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  HelloWorld -> C:\Workspace\CodeMaze\HelloWorld\bin\Debug\net7.0\HelloWorld.dll
  HelloWorld -> C:\Workspace\CodeMaze\HelloWorld\bin\Debug\net7.0\publish\

The publish command implicitly executed the build command and packaged all files needed to run the HelloWorld program in the publish folder mentioned in the last line. Let’s inspect the contents of that folder:

C:\Workspace\CodeMaze\HelloWorld>dir bin\Debug\net7.0\publish /B
Flurl.dll
HelloWorld.deps.json
HelloWorld.dll
HelloWorld.exe
HelloWorld.pdb
HelloWorld.runtimeconfig.json

Apart from the expected binary and symbolic files, we also see HelloWorld.deps.json and HelloWorld.runtimeconfig.json. As their extensions suggest, we use them for further automated dependency processing (optional) and runtime configuration, such as garbage collection mode (also optional). Note the Flurl.dll in the publish folder. The publish command will copy all dependencies into the output folder. This way, we don’t have to install the dependencies separately on the target machine.

We can use several arguments for the publish command to fine-tune the output for the specific environment to which we want to publish. For example, we might want to have different configurations for testing and for the production environment, or we might want to prepare deployments for multiple frameworks or architectures. For more information check out the dotnet publish command documentation.

Create the First Solution

The CLI allows us to create solution files that group multiple projects. First, let’s add one more project:

dotnet new classlib -o HelloWorldCore

Now we can create a new solution:

dotnet new sln -n HelloWorldSolution

The output is not very verbose, but it informs us that the solution file is created successfully:

The template "Solution File" was created successfully.

We don’t have much use for an empty solution so we should add the projects we created earlier:

C:\Workspace\CodeMaze>dotnet sln add .\HelloWorld\HelloWorld.csproj
Project `HelloWorld\HelloWorld.csproj` added to the solution.

C:\Workspace\CodeMaze>dotnet sln add .\HelloWorldCore\HelloWorldCore.csproj
Project `HelloWorldCore\HelloWorldCore.csproj` added to the solution.

Now, when we run the build command in the same directory:

dotnet build

We get some different output than we did with just one project:

MSBuild version 17.6.1+8ffc3fe3d for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  HelloWorld -> C:\Workspace\CodeMaze\HelloWorld\bin\Debug\net7.0\HelloWorld.dll
  HelloWorldCore -> C:\Workspace\CodeMaze\HelloWorldCore\bin\Debug\net7.0\HelloWorldCore.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Note that it now builds both projects, each in its respective output directory. 

Add the Project Reference

Most of the time, projects in a solution tend to have interdependencies. Let’s see how we can reference one project from another in the same solution.

First, we need to position ourselves in the referencing project folder. Then we can use the add reference command to add the dependency project:

C:\Workspace\CodeMaze>cd HelloWorld

C:\Workspace\CodeMaze\HelloWorld>dotnet add reference ..\HelloWorldCore\HelloWorldCore.csproj

When the command completes, we will see the output:

Reference `..\HelloWorldCore\HelloWorldCore.csproj` added to the project.

We can now use any classes defined in the HelloWorldCore project from the HelloWorld console application.

If we now run the publish command in the solution folder:

C:\Workspace\CodeMaze>dotnet publish

We can see that MSBuild built the projects from the solution and copied the publish output folder:

MSBuild version 17.6.1+8ffc3fe3d for .NET
  Determining projects to restore...
  Restored C:\Workspace\CodeMaze\HelloWorld\HelloWorld.csproj (in 238 ms).
  1 of 2 projects are up-to-date for restore.
  HelloWorldCore -> C:\Workspace\CodeMaze\HelloWorldCore\bin\Debug\net7.0\HelloWorldCore.dll
  HelloWorldCore -> C:\Workspace\CodeMaze\HelloWorldCore\bin\Debug\net7.0\publish\
  HelloWorld -> C:\Workspace\CodeMaze\HelloWorld\bin\Debug\net7.0\HelloWorld.dll
  HelloWorld -> C:\Workspace\CodeMaze\HelloWorld\bin\Debug\net7.0\publish\

Let’s inspect the publish output folder for the HelloWorld console application:

C:\Workspace\CodeMaze>dir HelloWorld\bin\Debug\net7.0\publish /B
Flurl.dll
HelloWorld.deps.json
HelloWorld.dll
HelloWorld.exe
HelloWorld.pdb
HelloWorld.runtimeconfig.json
HelloWorldCore.dll
HelloWorldCore.pdb

It contains all the dependencies needed to run the application, including the one we added.

Test the Solution From the CLI

The last (but not least) command that we will cover in this article is the test command. To test the solution we will first need to add a test project. We can use the new command to create one:

C:\Workspace\CodeMaze>dotnet new nunit -o HelloWorldTests
The template "NUnit 3 Test Project" was created successfully.

Processing post-creation actions...
Restoring C:\Workspace\CodeMaze\HelloWorldTests\HelloWorldTests.csproj:
  Determining projects to restore...
  Restored C:\Workspace\CodeMaze\HelloWorldTests\HelloWorldTests.csproj (in 4,8 sec).
Restore succeeded.

Then we will add it to the solution:

C:\Workspace\CodeMaze>dotnet sln add HelloWorldTests\HelloWorldTests.csproj
Project `HelloWorldTests\HelloWorldTests.csproj` added to the solution.

Because tests typically reference the library they are testing, let’s add the reference to HelloWorldCore project:

C:\Workspace\CodeMaze>CD HelloWorldTests

C:\Workspace\CodeMaze\HelloWorldTests>dotnet add reference ..\HelloWorldCore\HelloWorldCore.csproj
Reference `..\HelloWorldCore\HelloWorldCore.csproj` added to the project.

Now (from the solution directory) we are ready to test the solution:

dotnet test

We can expect an output similar to this:

  Determining projects to restore...
  Restored C:\Workspace\CodeMaze\HelloWorldTests\HelloWorldTests.csproj (in 205 ms).
  2 of 3 projects are up-to-date for restore.
  HelloWorldCore -> C:\Workspace\CodeMaze\HelloWorldCore\bin\Debug\net7.0\HelloWorldCore.dll
  HelloWorldTests -> C:\Workspace\CodeMaze\HelloWorldTests\bin\Debug\net7.0\HelloWorldTests.dll
Test run for C:\Workspace\CodeMaze\HelloWorldTests\bin\Debug\net7.0\HelloWorldTests.dll (.NETCoreApp,Version=v7.0)
Microsoft (R) Test Execution Command Line Tool Version 17.6.0 (x64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: 23 ms - HelloWorldTests.dll (net7.0)

The first part builds the test project along with all the references. The second part informs us of the directory from which the tests run, along with their outcomes. As we can see, the default test project template comes with one passing test. This can be very useful to verify that we have set up the environment correctly before we start writing actual unit tests.

In this example, we used an NUnit test project. We could also use the xUnit or MSTest template and we would see very similar output.

Similar to its sibling commands, the test command can also take a wide variety of arguments to further configure its execution, from test filtering to crash dump creation. For more details be sure to visit the dotnet test command documentation.

Conclusion

We’ve covered some basics about the main commands used to build and run .NET applications using only the CLI. Some of them, like new, run, sln, and add, are used in development loops, while creating the solution structure and writing the code. Others, like build, test, and publish, can be used in development loops, but also in scripts on the build server to automate publishing cycles or continuous integration. They can be very useful regardless of the IDE being used.

As we’ve seen from this brief introduction, the .NET CLI allows us to further enhance our development workflow and embrace the full potential of .NET. Let’s experiment with the .NET CLI in our projects, explore its extensive capabilities, and begin our journey towards more streamlined and effective .NET development.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!