Package Management

aaronontheweb/dotnet-skills/skills/package-management

by aarononthewebe426ed93a9f3No license1.2K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 3 weeks ago

Manage NuGet packages using Central Package Management (CPM) and dotnet CLI commands. Never edit XML directly - use dotnet add/remove/list commands. Use shared version variables for related packages.

Instructions onlySoftware Development
AI-generated overview

Guides NuGet package management in .NET projects using dotnet CLI commands and Central Package Management.

What it does
This skill provides instructions for adding, removing, updating, and listing NuGet packages through dotnet CLI commands rather than direct XML edits. It explains how to set up Central Package Management with a Directory.Packages.props file, use shared version variables for related packages, and configure package sources. It also covers troubleshooting restore failures, version conflicts, and lock files, plus common anti-patterns to avoid.
When to use it
Use it when adding, removing, or updating NuGet packages, setting up Central Package Management for a solution, managing versions across multiple projects, or troubleshooting package conflicts and restore issues.
Requirements
Requires the .NET SDK with the dotnet CLI; Central Package Management needs .NET SDK 6.0.300+, NuGet 6.2+, and Visual Studio 2022 17.2+. Private feeds may need credentials. No scripts are included; it is instructions only.

NuGet Package Management

When to Use This Skill

Use this skill when:

  • Adding, removing, or updating NuGet packages
  • Setting up Central Package Management (CPM) for a solution
  • Managing package versions across multiple projects
  • Troubleshooting package conflicts or restore issues

Golden Rule: Never Edit XML Directly

Always use dotnet CLI commands to manage packages. Never manually edit .csproj or Directory.Packages.props files.

bash
# DO: Use CLI commandsdotnet add package Newtonsoft.Jsondotnet remove package Newtonsoft.Jsondotnet list package --outdated
# DON'T: Edit XML directly# <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

Why:

  • CLI validates package exists and resolves correct version
  • Handles transitive dependencies correctly
  • Updates lock files if present
  • Avoids typos and malformed XML
  • Works correctly with CPM

Central Package Management (CPM)

CPM centralizes all package versions in one file, eliminating version conflicts across projects.

Enable CPM

Create Directory.Packages.props in solution root:

xml
<Project>  <PropertyGroup>    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>  </PropertyGroup>
  <ItemGroup>    <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />    <PackageVersion Include="Serilog" Version="4.0.0" />    <PackageVersion Include="xunit" Version="2.9.2" />  </ItemGroup></Project>

Project Files with CPM

Projects reference packages without versions:

xml
<!-- src/MyApp/MyApp.csproj --><Project Sdk="Microsoft.NET.Sdk">  <ItemGroup>    <PackageReference Include="Newtonsoft.Json" />    <PackageReference Include="Serilog" />  </ItemGroup></Project>

Adding Packages with CPM

bash
# Adds to Directory.Packages.props AND project filedotnet add package Serilog.Sinks.Console
# Result in Directory.Packages.props:# <PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
# Result in project file:# <PackageReference Include="Serilog.Sinks.Console" />

Shared Version Variables

Group related packages with shared version variables:

xml
<Project>  <PropertyGroup>    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>  </PropertyGroup>
  <!-- Shared version variables -->  <PropertyGroup Label="SharedVersions">    <AkkaVersion>1.5.59</AkkaVersion>    <AkkaHostingVersion>1.5.59</AkkaHostingVersion>    <AspireVersion>9.0.0</AspireVersion>    <OpenTelemetryVersion>1.11.0</OpenTelemetryVersion>    <XunitVersion>2.9.2</XunitVersion>  </PropertyGroup>
  <!-- Akka.NET packages - all use same version -->  <ItemGroup Label="Akka.NET">    <PackageVersion Include="Akka" Version="$(AkkaVersion)" />    <PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />    <PackageVersion Include="Akka.Cluster.Sharding" Version="$(AkkaVersion)" />    <PackageVersion Include="Akka.Cluster.Tools" Version="$(AkkaVersion)" />    <PackageVersion Include="Akka.Persistence" Version="$(AkkaVersion)" />    <PackageVersion Include="Akka.Streams" Version="$(AkkaVersion)" />    <PackageVersion Include="Akka.Hosting" Version="$(AkkaHostingVersion)" />    <PackageVersion Include="Akka.Cluster.Hosting" Version="$(AkkaHostingVersion)" />  </ItemGroup>
  <!-- Aspire packages -->  <ItemGroup Label="Aspire">    <PackageVersion Include="Aspire.Hosting" Version="$(AspireVersion)" />    <PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireVersion)" />    <PackageVersion Include="Aspire.Hosting.PostgreSQL" Version="$(AspireVersion)" />    <PackageVersion Include="Aspire.Hosting.Testing" Version="$(AspireVersion)" />  </ItemGroup>
  <!-- OpenTelemetry packages -->  <ItemGroup Label="OpenTelemetry">    <PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="$(OpenTelemetryVersion)" />    <PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="$(OpenTelemetryVersion)" />    <PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="$(OpenTelemetryVersion)" />    <PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="$(OpenTelemetryVersion)" />  </ItemGroup>
  <!-- Testing -->  <ItemGroup Label="Testing">    <PackageVersion Include="xunit" Version="$(XunitVersion)" />    <PackageVersion Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />    <PackageVersion Include="FluentAssertions" Version="6.12.0" />    <PackageVersion Include="Verify.Xunit" Version="26.0.0" />  </ItemGroup></Project>

Benefits:

  • Update all Akka packages by changing one variable
  • Clear organization with labeled ItemGroups
  • Prevents version mismatches in related packages

When NOT to Use CPM

Central Package Management isn't always the right choice:

Legacy Projects

Migrating an existing large solution to CPM can introduce issues:

  • Existing version conflicts become visible all at once
  • Some packages may have intentional version differences
  • Migration requires touching many files simultaneously

Recommendation: For legacy projects, migrate incrementally or stick with per-project versioning if it's working.

Version Ranges

CPM requires exact versions - it doesn't support version ranges:

xml
<!-- NOT supported with CPM --><PackageVersion Include="Newtonsoft.Json" Version="[13.0,14.0)" />
<!-- Must use exact version --><PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />

If you need version ranges (rare, but some library scenarios require it), CPM won't work.

Older .NET Versions

CPM requires:

  • .NET SDK 6.0.300+ or later
  • NuGet 6.2+ or later
  • Visual Studio 2022 17.2+ or later

If you're targeting older SDK versions or have team members on older tooling, CPM may cause build failures.

Multi-Repo Solutions

If your solution spans multiple repositories that are built independently, CPM's single Directory.Packages.props won't help - each repo needs its own.


CLI Command Reference

Adding Packages

bash
# Add latest stable versiondotnet add package Serilog
# Add specific versiondotnet add package Serilog --version 4.0.0
# Add prereleasedotnet add package Serilog --prerelease
# Add to specific projectdotnet add src/MyApp/MyApp.csproj package Serilog

Removing Packages

bash
# Remove from current projectdotnet remove package Serilog
# Remove from specific projectdotnet remove src/MyApp/MyApp.csproj package Serilog

Listing Packages

bash
# List all packages in solutiondotnet list package
# Show outdated packagesdotnet list package --outdated
# Include transitive dependenciesdotnet list package --include-transitive
# Show vulnerable packagesdotnet list package --vulnerable
# Show deprecated packagesdotnet list package --deprecated

Updating Packages

bash
# With CPM: Edit the version in Directory.Packages.props# Then restore to applydotnet restore
# Without CPM: Remove and add with new versiondotnet remove package Serilogdotnet add package Serilog --version 4.1.0
# Or use dotnet-outdated tool (recommended)dotnet tool install --global dotnet-outdated-tooldotnet outdated --upgrade

Restore and Clean

bash
# Restore packagesdotnet restore
# Clear local cache (troubleshooting)dotnet nuget locals all --clear
# Force restore (ignore cache)dotnet restore --force

Package Sources

List Sources

bash
dotnet nuget list source

Add Private Feed

bash
# Add authenticated feeddotnet nuget add source https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json \  --name MyFeed \  --username az \  --password $PAT \  --store-password-in-clear-text

NuGet.config

For solution-specific sources, create NuGet.config:

xml
<?xml version="1.0" encoding="utf-8"?><configuration>  <packageSources>    <clear />    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />    <add key="MyPrivateFeed" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" />  </packageSources>  <packageSourceCredentials>    <MyPrivateFeed>      <add key="Username" value="az" />      <add key="ClearTextPassword" value="%NUGET_PAT%" />    </MyPrivateFeed>  </packageSourceCredentials></configuration>

Common Patterns

Development-Only Packages

xml
<!-- Directory.Packages.props --><PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<!-- Project file - mark as development dependency --><PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />

Conditional Packages

xml
<!-- Only include in Debug builds --><ItemGroup Condition="'$(Configuration)' == 'Debug'">  <PackageReference Include="JetBrains.Annotations" /></ItemGroup>
<!-- Platform-specific --><ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">  <PackageReference Include="System.Text.Json" /></ItemGroup>

Version Override (Escape Hatch)

When you must override CPM for one project (rare):

xml
<!-- Project file - use sparingly! --><PackageReference Include="Newtonsoft.Json" VersionOverride="12.0.3" />

Warning: This is detected by Slopwatch (see dotnet/slopwatch skill) as potential slop.


Troubleshooting

Version Conflicts

bash
# See full dependency treedotnet list package --include-transitive
# Find what's pulling in a specific packagedotnet list package --include-transitive | grep -i "PackageName"

Restore Failures

bash
# Clear all cachesdotnet nuget locals all --clear
# Restore with detailed loggingdotnet restore --verbosity detailed
# Check for locked packagescat packages.lock.json

Lock Files

For reproducible builds, use package lock files:

xml
<!-- Directory.Build.props --><PropertyGroup>  <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile></PropertyGroup>

Then commit packages.lock.json files.


Anti-Patterns

Don't: Edit XML Directly

xml
<!-- BAD: Manual XML editing --><PackageReference Include="Typo.Package" Version="1.0.0" /><!-- Package might not exist! CLI would catch this. -->

Don't: Inline Versions with CPM

xml
<!-- BAD: Bypasses CPM --><PackageReference Include="Serilog" Version="4.0.0" />
<!-- GOOD: Version comes from Directory.Packages.props --><PackageReference Include="Serilog" />

Don't: Mix Version Management

xml
<!-- BAD: Some versions in CPM, some inline --><PackageReference Include="Serilog" />  <!-- From CPM --><PackageReference Include="Newtonsoft.Json" Version="13.0.3" />  <!-- Inline -->

Don't: Forget Shared Variables

xml
<!-- BAD: Related packages with different versions --><PackageVersion Include="Akka" Version="1.5.59" /><PackageVersion Include="Akka.Cluster" Version="1.5.58" />  <!-- Mismatch! -->
<!-- GOOD: Use shared variable --><PackageVersion Include="Akka" Version="$(AkkaVersion)" /><PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />

Quick Reference

TaskCommand
Add packagedotnet add package <name>
Add specific versiondotnet add package <name> --version <ver>
Remove packagedotnet remove package <name>
List packagesdotnet list package
Show outdateddotnet list package --outdated
Show vulnerabledotnet list package --vulnerable
Restoredotnet restore
Clear cachedotnet nuget locals all --clear

Resources

Source and attribution

Source:aaronontheweb/dotnet-skillsinskills/package-managementat commite426ed9

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal