Skip to main content

How to Use Central Package Management for NuGet in a .NET C# Solution (Directory.Packages.props)

· 6 min read
Jagdish Kumawat
Founder @ Dewiride

In a solution with several projects, NuGet package versions drift: one project on EF Core 10.0.12, another on 10.0.14, and nobody notices until something breaks. Central Package Management moves every version into one file at the root, Directory.Packages.props, so each package has one version for the whole solution. This guide sets it up, moves existing versions into it, and shows the CLI commands that maintain it.

How It Works​

The NuGet docs describe three pieces:

  1. "create a Directory.Packages.props file at the root of your repository and set the MSBuild property ManagePackageVersionsCentrally to true."
  2. "Inside Directory.Packages.props, define <PackageVersion /> elements to specify the package IDs and versions used by your projects."
  3. "In each project file, define <PackageReference /> elements without the Version attribute."

So projects still say which packages they use, and the central file says which version.

Prerequisites​

Step 1: Create Directory.Packages.props​

In the solution's root folder:

Terminal
dotnet new packagesprops
Output
The template "MSBuild Directory.Packages.props file" was created successfully.
Directory.Packages.props
<Project>
<PropertyGroup>
<!-- Enable central package management, https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
</Project>

A terminal where dotnet new packagesprops creates Directory.Packages.props, which sets ManagePackageVersionsCentrally to true and has an empty ItemGroup for package versions.

Nothing else is needed to switch it on. MSBuild finds the file by looking upward from each project's folder, so it applies to every project below the root. The docs add: "If you have multiple Directory.Packages.props files in your repository, the file closest to a given project's directory will be evaluated for it."

Step 2: Move Existing Versions into It​

Projects that already have packages still carry Version="…" on their PackageReference items. With central management on, restore refuses them:

Terminal
dotnet restore
Output
/Users/you/projects/dewiride-inventory-api/src/Dewiride.Inventory.Api/Dewiride.Inventory.Api.csproj : error NU1008: The following PackageReference items cannot define a value for Version: Microsoft.AspNetCore.OpenApi. Projects using Central Package Management must define a Version value on a PackageVersion item. For more information, visit https://aka.ms/nuget/cpm/gettingstarted

Restore failed with 1 error(s) in 0.7s

For each package, move the version: a <PackageVersion> in the central file:

Directory.Packages.props (the ItemGroup)
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.14" />
</ItemGroup>

and no Version in the project:

src/Dewiride.Inventory.Api/Dewiride.Inventory.Api.csproj (excerpt)
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
Output (dotnet restore)
Restore complete (1.6s)

Build succeeded in 1.7s

Fixed: error NU1008 covers this error in detail, including the dotnet new webapi template that causes it.

Step 3: Add Packages with the CLI​

dotnet package add knows about central management. The command reference: "If the project is onboarded onto Central Package Management (CPM) the <PackageVersion> element in the Directory.Packages.props file is added/updated and the <PackageReference> element is added to the project file."

Terminal
dotnet package add MySql.EntityFrameworkCore --version 10.0.12 --project src/Dewiride.Inventory.Data
Output (the lines that matter)
info : PackageReference for package 'MySql.EntityFrameworkCore' added to '/Users/you/projects/dewiride-inventory-api/src/Dewiride.Inventory.Data/Dewiride.Inventory.Data.csproj' and PackageVersion added to central package management file '/Users/you/projects/dewiride-inventory-api/Directory.Packages.props'.
info : PackageReference for package 'MySql.EntityFrameworkCore' version '10.0.12' added to file '/Users/you/projects/dewiride-inventory-api/Directory.Packages.props'.

A terminal where dotnet package add MySql.EntityFrameworkCore --version 10.0.12 adds the PackageReference to the Data project and the PackageVersion 10.0.12 to Directory.Packages.props.

One command, two files: the reference in the project, the version in the central file.

Step 4: Update a Version in One Place​

To update a package for every project that uses it, change its PackageVersion, by hand or with the same command and a new --version. After updating Newtonsoft.Json to 13.0.4 that way, the API resolved the central version:

Terminal
dotnet package list --project src/Dewiride.Inventory.Api/Dewiride.Inventory.Api.csproj
Output (last lines)
Top-level Package Requested Resolved
> Microsoft.AspNetCore.OpenApi 10.0.14 10.0.14
> Newtonsoft.Json 13.0.4 13.0.4

Step 5: Override One Project's Version​

Sometimes one project has to stay on another version. The docs: "You can override an individual package version by using the VersionOverride property on a <PackageReference /> item. This will take precedence over any centrally defined <PackageVersion />."

src/Dewiride.Inventory.Api/Dewiride.Inventory.Api.csproj (excerpt)
<PackageReference Include="Newtonsoft.Json" VersionOverride="13.0.3" />
Output (dotnet package list, last lines)
Top-level Package Requested Resolved
> Microsoft.AspNetCore.OpenApi 10.0.14 10.0.14
> Newtonsoft.Json 13.0.3 13.0.3

The API used 13.0.3 while the central file still said 13.0.4. Use it sparingly, with a comment saying why: every override is a place where the solution has two versions again. I removed it after the test.

Common Mistakes​

MistakeWhat happensDo this instead
Leaving Version on a PackageReferenceRestore fails with NU1008Move the version to Directory.Packages.props
Naming the file directory.packages.propsWorks on Windows and on macOS's default file system, but isn't found on Linux, whose file names are case-sensitiveUse Directory.Packages.props exactly
A PackageVersion for a package no project usesHarmless, but the file stops reflecting realityRemove versions you no longer need
VersionOverride without a commentNobody knows later why one project differsExplain it next to the override

FAQ​

Does Central Package Management change which packages a project gets?​

No. A project gets only the packages it references with PackageReference; the central file only supplies versions. A PackageVersion alone doesn't add a package anywhere.

Can the central file also control packages I don't reference directly?​

Yes, with transitive pinning. How to Upgrade a Transitive NuGet Package with Central Package Management shows it.

Conclusion​

dotnet new packagesprops creates Directory.Packages.props with ManagePackageVersionsCentrally on. From then on, every package version lives there as a <PackageVersion>, projects reference packages without versions, dotnet package add writes to both files, and one edit updates a package for the whole solution. Keep VersionOverride for the rare project that really needs another version.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.