Projects, builds and NuGet
Every .NET app is a project: a folder plus a .csproj file that MSBuild reads. Follow an app that uses a library project of your own (a ProjectReference) and the NuGet package Newtonsoft.Json (a PackageReference): restore picks version 13.0.3 and the lib/net6.0 DLL, MSBuild builds Greeter first and gathers three DLLs in App/bin/Debug/net8.0/, a rebuild skips what is up to date, and dotnet pack turns your library into a package.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01App.csproj says <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />. nuget.org has 13.0.3 and a newer stable version, 13.0.4. Which one does restore pick?
A bare version is a minimum (13.0.3 or higher), and NuGet resolves to the lowest applicable version. So 13.0.4 is not picked. That keeps restores repeatable: a new release on the feed does not change your build.
02What is the difference between a ProjectReference and a PackageReference?
ProjectReference makes MSBuild build the other project (first) and use its DLL. PackageReference makes restore fetch a .nupkg from a feed and use the DLL inside it.
03The package holds lib/net45, lib/net6.0 and lib/netstandard2.0 (among others). App targets net8.0. Which DLL does it use?
There is no lib/net8.0 folder, so NuGet takes the nearest framework net8.0 is compatible with: net6.0. .NET Standard 2.0 also works but is further away; net45 is for the old .NET Framework.
04You change one line in App/Program.cs and run dotnet build again. What happens to the Greeter project?
MSBuild targets compare their inputs with their outputs and skip the work when the outputs are up to date. That is what makes rebuilding a large solution fast.
05What does dotnet pack make from the Greeter project (with no version set)?
The package ID defaults to the project's name and the version to 1.0.0, so you get Greeter.1.0.0.nupkg (in Greeter/bin/Release/, since dotnet pack uses Release by default from the .NET 8 SDK). dotnet nuget push can upload it to a feed.
Glossary
- Project file (.csproj)
- An XML file that MSBuild reads: which SDK to use, the target framework, the output type and the references.
- MSBuild
- The .NET build engine. dotnet build, dotnet restore and dotnet pack all run MSBuild targets.
- NuGet package (.nupkg)
- A zip file with compiled DLLs (one folder per target framework) and a description: id, version and dependencies.
- ProjectReference
- A reference to another project; MSBuild builds that project first and uses its DLL.
- PackageReference
- A reference to a NuGet package by id and version; restore downloads it from a feed such as nuget.org.
- Restore
- The step that resolves package versions, downloads packages into the global packages folder (~/.nuget/packages) and writes obj/project.assets.json.
- Target framework (TFM)
- The .NET version a project builds for, written as a short name such as net8.0 or netstandard2.0.
- Incremental build
- A build that skips every step whose outputs are already newer than its inputs.
Notes and sources
- The App.csproj card leaves out the PropertyGroup (OutputType Exe, TargetFramework net8.0) and the ItemGroup tags (the "…" lines); the step text shows the whole file. Splitting a tag over lines is ordinary XML.
- dotnet restore also checks every package's own dependencies (Newtonsoft.Json 13.0.3 has none for net6.0) and, from the .NET 8 SDK, warns about packages with known security problems. A second restore with nothing changed does almost no work.
- If 13.0.3 were missing from the feed, NuGet would take the next higher version and warn (NU1603). Write Version="[13.0.3]" to allow only 13.0.3.
- Besides the three DLLs, App/bin/Debug/net8.0/ holds App.deps.json (which DLL comes from which package), App.runtimeconfig.json (which .NET runtime to use), App.pdb (debug symbols) and a small launcher, App on macOS and Linux or App.exe on Windows.
- Skipping is per MSBuild target: the C# compile target (CoreCompile) runs only when an input file is newer than its outputs. Greeter is still visited but does no compiling. The .NET SDK also builds reference assemblies, so a change inside a Greeter method body may not even make App recompile.
- Package IDs on nuget.org are first come, first served, so a real package needs a unique ID such as YourName.Greeter (set <PackageId>). dotnet nuget push needs an API key from your nuget.org account.
- The projects are real: with a .NET 8 SDK, dotnet run in App/ prints {"Text":"Hello, NuGet!"}.
- Microsoft Learn: .NET project SDKs
- Microsoft Learn: MSBuild
- Microsoft Learn: Package references (PackageReference) in project files
- Microsoft Learn: dotnet restore
- Microsoft Learn: NuGet package versioning (version ranges)
- Microsoft Learn: How NuGet resolves package dependencies
- Microsoft Learn: Managing the global packages and cache folders
- Microsoft Learn: Target frameworks in SDK-style projects
- Microsoft Learn: dotnet build
- Microsoft Learn: Incremental builds (MSBuild)
- Microsoft Learn: dotnet pack
- Microsoft Learn: dotnet pack uses the Release configuration (.NET 8)
- nuget.org: Newtonsoft.Json 13.0.3
Quiz Unlocks after the last step.