NuGet is .NET’s package manager — the ecosystem of over 300,000 reusable libraries available from nuget.org. A package bundles compiled DLLs, targets files, and metadata into a versioned .nupkg archive. Package management is a critical operational skill: choosing the right packages, pinning versions for reproducibility, understanding transitive dependencies, and keeping packages up to date without breaking changes. This series uses several key NuGet packages throughout — knowing how to find, install, and update them is essential before Part 2.
Installing and Managing Packages
// ── Install a package (adds PackageReference to .csproj) ─────────────────
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 8.0.2
dotnet add package Serilog.AspNetCore --version 8.0.0
// ── Remove a package ──────────────────────────────────────────────────────
dotnet remove package Newtonsoft.Json
// ── Restore packages (download if missing) ────────────────────────────────
dotnet restore // restores all projects in solution
dotnet restore src/BlogApp.Api/BlogApp.Api.csproj // restore one project
// ── List packages and check for updates ───────────────────────────────────
dotnet list package // list installed packages
dotnet list package --outdated // show packages with newer versions
dotnet list package --vulnerable // show packages with known vulnerabilities
// ── Update a package ──────────────────────────────────────────────────────
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 8.0.3
// ── Search NuGet.org from CLI ─────────────────────────────────────────────
dotnet package search "serilog" --source https://api.nuget.org/v3/index.json
MAJOR.MINOR.PATCH. A MAJOR version bump signals breaking changes; a MINOR version adds functionality backwards-compatibly; a PATCH version fixes bugs. When specifying package versions, do not use wildcards (8.*) in production — pin to exact versions or use version ranges carefully. An unexpected major version update can introduce breaking API changes that cause subtle runtime errors without compile-time errors. The dotnet list package --outdated command helps you identify available updates safely.<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> in Directory.Build.props) to ensure reproducible builds. The lock file (packages.lock.json) records the exact resolved version of every package (including transitive dependencies) at restore time. Commit the lock file to version control. CI/CD can then use dotnet restore --locked-mode to verify the build uses exactly the same packages as the developer who last updated the lock file — no surprise dependency upgrades in CI.dotnet list package --vulnerable in your CI pipeline and fail the build if vulnerable packages are detected. NuGet integrates with the GitHub Advisory Database and the OSV database to flag known CVEs. Do not delay patching vulnerabilities in direct dependencies; vulnerable transitive dependencies may require updating the direct dependency that pulls them in. Automated dependency update tools like Dependabot handle this in GitHub repositories.Central Package Management
// ── Directory.Packages.props — single place for ALL package versions ───────
// Place in the solution root; applies to every project in the solution
<!-- Directory.Packages.props -->
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- EF Core -->
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2" />
<!-- Logging -->
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="5.0.1" />
<!-- Validation -->
<PackageVersion Include="FluentValidation" Version="11.9.0" />
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
<!-- Testing -->
<PackageVersion Include="xunit" Version="2.7.0" />
<PackageVersion Include="Moq" Version="4.20.70" />
</ItemGroup>
</Project>
// ── In project .csproj files — version is now omitted ────────────────────
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" /> <!-- no version! -->
<PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="FluentValidation.AspNetCore" />
</ItemGroup>
Key Packages Used in This Series
| Package | Purpose | Chapter |
|---|---|---|
| Microsoft.EntityFrameworkCore.SqlServer | ORM for SQL Server | 37–38 |
| Serilog.AspNetCore | Structured logging | 20 |
| FluentValidation.AspNetCore | Model validation | 36 |
| MediatR | CQRS mediator pattern | 85 |
| AutoMapper | Object-to-object mapping | 35 |
| Swashbuckle.AspNetCore | Swagger/OpenAPI docs | 42 |
| xunit + Moq | Unit testing framework | 78–79 |
Common Mistakes
Mistake 1 — Using different package versions across projects (version drift)
❌ Wrong — BlogApp.Api uses EF Core 8.0.0 while BlogApp.Infrastructure uses 8.0.2 (different versions of the same assembly loaded at runtime).
✅ Correct — use Central Package Management to enforce one version per package across all projects.
Mistake 2 — Not restoring packages in CI (build fails with missing DLLs)
❌ Wrong — CI pipeline runs dotnet build without dotnet restore first (packages not cached).
✅ Correct — run dotnet restore as a separate step before dotnet build, or use dotnet build –no-restore after an explicit restore step.