100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET

NuGet Package Management

How .NET Core projects declare, resolve, and centrally manage third-party dependencies through NuGet.

Building BlocksBeginner9 min readJul 10, 2026
Analogies

What NuGet Packages Are

NuGet is the package manager for .NET, distributing reusable libraries as .nupkg files that bundle compiled assemblies, metadata, and sometimes build logic or analyzers. Packages are pulled from feeds — nuget.org by default, or private feeds like Azure Artifacts or GitHub Packages — and cached locally in the global-packages folder (typically ~/.nuget/packages) so multiple projects on the same machine share a single copy instead of duplicating binaries.

🏏

Cricket analogy: This is like a central kit room shared by an entire franchise such as Mumbai Indians, where equipment (bats, pads) is stored once and issued to whichever squad (project) needs it, instead of every player buying and storing duplicate gear, the way NuGet's global-packages cache avoids duplicating the same package per project.

PackageReference and Version Resolution

Dependencies are declared with <PackageReference Include="..." Version="..." /> elements inside an ItemGroup. NuGet resolves the full dependency graph transitively — if your project references Serilog.AspNetCore, NuGet also pulls in Serilog and Serilog.Sinks.Console automatically — and when two packages request different versions of the same transitive dependency, NuGet applies the 'nearest wins' rule unless you override it explicitly, which can silently upgrade or downgrade a package you never referenced directly.

🏏

Cricket analogy: This is like a team management structure where the captain (your direct PackageReference) brings in a specialist coach, who in turn brings their own assistant coaches (transitive dependencies) without the captain hiring each assistant individually, the way referencing Serilog.AspNetCore transitively pulls in Serilog itself.

bash
dotnet add package Serilog.AspNetCore --version 8.0.1
dotnet restore
dotnet list package --include-transitive

<ItemGroup>
  <PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.6" />
</ItemGroup>

Central Package Management and Lock Files

In a solution with many projects, Central Package Management (CPM) lets you move all Version attributes into a single Directory.Packages.props file at the solution root, with each .csproj declaring only <PackageReference Include="..." /> without a version — MSBuild resolves the version centrally, so upgrading a package solution-wide is a one-line change instead of hunting through dozens of .csproj files. For reproducible builds, a packages.lock.json file (enabled via <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>) pins the exact resolved graph, including transitive versions, so restore produces identical results on every machine and in CI until the lock file is deliberately updated.

🏏

Cricket analogy: This is like a franchise setting one central salary cap policy for the whole organization instead of each age-group team negotiating separately, so a policy change at the top (Directory.Packages.props) updates every team at once, the way CPM updates a version across every project from one file.

Run 'dotnet list package --outdated' periodically to see which of your direct dependencies have newer versions available, and 'dotnet list package --vulnerable' to check for known security advisories reported against your restored packages — both read from the same restore metadata NuGet already produced.

Floating versions like Version="8.*" or Version="[8.0.0,)" make builds non-reproducible: the exact resolved version can differ between your machine and CI depending on what was published to the feed that day. Pin exact versions, or better, commit a packages.lock.json so restores are deterministic everywhere.

  • NuGet packages (.nupkg files) bundle assemblies and metadata, fetched from feeds like nuget.org and cached in a shared global-packages folder.
  • PackageReference elements declare direct dependencies; NuGet resolves the full transitive dependency graph automatically.
  • When versions conflict, NuGet's 'nearest wins' algorithm picks a resolved version, which can silently affect transitive packages.
  • Central Package Management (Directory.Packages.props) centralizes version numbers for an entire solution in one file.
  • packages.lock.json pins the exact resolved dependency graph for reproducible restores across machines and CI.
  • dotnet list package --outdated and --vulnerable surface upgrade and security information from existing restore metadata.
  • Avoid floating version ranges in production projects; they undermine reproducible builds.

Practice what you learned

Was this page helpful?

Topics covered

#NET#NETCoreStudyNotes#MicrosoftTechnologies#NuGetPackageManagement#NuGet#Package#Management#Packages#StudyNotes#SkillVeris