Skip to content
Educora
Advanced20 min11 / 16

The .NET CLI, projects and NuGet

Create, build, run and test projects with the `dotnet` command, combine them in a solution and add NuGet packages.

Check yourself
In this lesson you will learn
  • Use the dotnet new, build, run and test commands
  • Read an SDK-style .csproj file and set up project references in a solution
  • Add a NuGet package and explain where it is stored

In the C# course every program was a single app.cs file, started with dotnet run app.cs thanks to a .NET 10 feature. A real application, however, consists of several projects: a library with the business logic, a console or web app that uses it, and tests. To create them, connect them, build them and add external packages there is the .NET CLI — the dotnet command. It works the same on Windows, macOS and Linux.

The main .NET CLI commands

CommandWhat it does
dotnet new listlists the available templates
dotnet new console -n Hellocreates a console project named Hello
dotnet buildcompiles the project (into the bin/ and obj/ folders)
dotnet runbuilds and runs right away
dotnet testruns the test projects
dotnet add package Xadds a NuGet package
dotnet publish -c Releasebuilds a ready-to-ship version for a server or users
dotnet --infoshows the installed SDKs and runtimes
Terminal
dotnet new console -n Hello
cd Hello
dotnet run
Expected output
The template "Console App" was created successfully.
...
Restore succeeded.
Hello, World!
Real output (shortened): the template was created, packages were restored and the program ran

The heart of a project is the **.csproj file — XML in MSBuild format. A modern SDK-style** file contains only what matters: TargetFramework (net10.0), ImplicitUsings (adds frequently needed using directives automatically) and Nullable (warnings about null mistakes). All .cs files in the folder are included automatically. In old .NET Framework projects every file had to be listed separately:

.NET Framework (old style)
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Student.cs" />
    <Compile Include="GradeCalculator.cs" />
    <!-- every new file had to be listed here -->
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
SDK style (.NET 10)
<Project Sdk="Microsoft.NET.Sdk">

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

  <ItemGroup>
    <ProjectReference Include="..\Gradebook.Core\Gradebook.Core.csproj" />
    <PackageReference Include="Humanizer" Version="2.14.1" />
  </ItemGroup>

</Project>
The new file also has a project reference and a NuGet package — both are explained below

Solutions: several projects together

A solution is a file that groups related projects; in .NET 10 it is created by default in the new, compact .slnx format (the old .sln still works). A typical structure: Gradebook.Core — a library that depends on nothing, Gradebook.App — a console program that calls it, Gradebook.Tests — xUnit tests. A project reference (ProjectReference) lets one project see another project's classes.

  1. 1
    Create the solution and the projects

    dotnet new sln -n Gradebook, then three projects from the classlib, console and xunit templates.

  2. 2
    Add the projects to the solution

    dotnet sln add ... — now the IDE and dotnet build see all three projects together.

  3. 3
    Set up the references

    dotnet add Gradebook.App reference Gradebook.Core and the same command for the tests. A reference goes one way: Core does not see App.

  4. 4
    Build, run, test

    In the solution folder dotnet build builds everything, dotnet run --project Gradebook.App starts the console app and dotnet test runs the tests.

Terminal
dotnet new sln -n Gradebook
dotnet new classlib -n Gradebook.Core
dotnet new console -n Gradebook.App
dotnet new xunit -n Gradebook.Tests
dotnet sln add Gradebook.Core Gradebook.App Gradebook.Tests
dotnet add Gradebook.App reference Gradebook.Core
dotnet add Gradebook.Tests reference Gradebook.Core
All the steps as commands — the result is Gradebook.slnx and three project folders
C#
namespace Gradebook.Core;

public static class GradeCalculator
{
    public static double Average(IEnumerable<int> scores)
    {
        var list = scores.ToList();
        if (list.Count == 0) throw new ArgumentException("No scores");
        return list.Average();
    }

    public static string Letter(int score) => score switch
    {
        < 0 or > 100 => throw new ArgumentOutOfRangeException(nameof(score)),
        >= 90 => "A",
        >= 80 => "B",
        >= 70 => "C",
        >= 50 => "D",
        _ => "F"
    };
}
Gradebook.Core/GradeCalculator.cs — a class in the library (it must be public for another project to see it)
C#
using System.Globalization;
using Gradebook.Core;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

int[] scores = [92, 78, 85];
double average = GradeCalculator.Average(scores);
Console.WriteLine($"Average: {average:F1}");
Console.WriteLine($"Letter: {GradeCalculator.Letter((int)average)}");
Expected output
Average: 85.0
Letter: B
Gradebook.App/Program.cs, run with dotnet run --project Gradebook.App

NuGet packages

NuGet is the .NET package manager, and nuget.org is the repository of hundreds of thousands of ready-made libraries (like Maven Central in Java). dotnet add package Humanizer writes the latest version into the .csproj as a PackageReference; in .NET 10 the new-style dotnet package add Humanizer does the same. dotnet build or dotnet restore downloads the packages and keeps them in a shared cache in your user folder (~/.nuget/packages), and dotnet list package --outdated shows outdated packages.

C#
using Humanizer;

Console.WriteLine("score".ToQuantity(1));
Console.WriteLine("score".ToQuantity(3));
Console.WriteLine("student".ToQuantity(12));
Expected output
1 score
3 scores
12 students
Sample output: the Humanizer package puts words into the plural by English rules (it does not work without the package)

To share a finished program, use dotnet publish: since .NET 8 it builds by default in the Release configuration, which optimises the code (dotnet build uses Debug mode, meant for debugging). Add -r win-x64 --self-contained and the result is a stand-alone folder that runs even on a computer without .NET installed. So that everyone on the team works with the same SDK version, a global.json file is placed at the root of the repository — dotnet new globaljson creates it.

Key points

  • dotnet new, build, run, test, publish — one CLI for a project's whole life.
  • An SDK-style .csproj: TargetFramework, ImplicitUsings, Nullable; .cs files are included automatically.
  • A solution (.slnx) groups projects, and ProjectReference connects them in one direction.
  • NuGet: dotnet add package → PackageReference; packages are kept in the ~/.nuget/packages cache.
  • With several projects write dotnet run --project ...; bin/ and obj/ do not go into Git.

Check yourself

10 questions. Every correct answer earns XP.

1 / 10
Which command creates a class library?