Centralized Package Management in .NET Core


Problem โ
In multi-project .NET solutions, developers face the following challenges:
Inconsistent package versions: Different projects reference different versions of the same package.
Version conflicts: Resolving version mismatch issues is time-consuming and error-prone.
Duplication: Updating package versions across multiple
.csproj
files is tedious.
Without centralized management, maintenance becomes complex, especially in microservice architectures or layered applications.
Solution ๐ก
Centralized Package Management (CPM) allows developers to define package versions in a single Directory.Packages.props
file. Introduced in .NET 5 and enhanced in later versions, this feature improves maintainability and avoids duplication.
With CPM, you:
Define package versions once.
Consume packages in project files without repeating versions.
Maintain consistency across all projects in your solution.
How It Works โ๏ธ
The core idea of CPM is to separate versioning from referencing. Here's how it works:
Create a
Directory.Packages.props
file in the solution root.Add package versions using
<PackageVersion>
elements.Reference packages in project files without specifying the version.
MSBuild automatically merges the version from the centralized file during build.
Example Directory.Packages.props
:
<Project>
<ItemGroup>
<PackageVersion Include="Serilog" Version="2.12.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup>
</Project>
Example MyApp.csproj
:
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Serilog" />
<PackageReference Include="Microsoft.Extensions.Logging" />
</ItemGroup>
</Project>
Benefit ๐ฏ
Using CPM brings several key advantages:
Consistency: All projects use the same package versions.
Maintainability: Update versions in one place.
Reduced Conflicts: Eliminate ambiguity in dependency resolution.
Cleaner Project Files: Project files focus on purpose, not versions.
Support for Transitive Pinning: Control transitive package versions explicitly.
Implement ๐
Follow these steps to enable CPM in your solution:
Create the configuration file:
Crate the
Directory.Packages.props
at the root folder of the solutiontouch Directory.Packages.props
Add package versions:
<Project> <ItemGroup> <PackageVersion Include="AutoMapper" Version="12.0.0" /> <PackageVersion Include="FluentValidation" Version="11.5.1" /> </ItemGroup> </Project>
Update
.csproj
files: RemoveVersion
attributes from<PackageReference>
elements.Build the solution: MSBuild will now resolve versions from
Directory.Packages.props
.
Advanced ๐ง
๐ Overriding Package Versions
In some cases, you may want to override the centralized version in a specific project. To do this, explicitly specify the version using VersionOverride
in the project file:
<ItemGroup>
<PackageReference Include="Serilog" VersionOverride="2.11.0" />
</ItemGroup>
Note: MSBuild uses the closest version definition when resolving dependencies. A version declared in a
.csproj
will override what's inDirectory.Packages.props
.
๐ Global Package References
You can define global package references in the Directory.Packages.props
file using <GlobalPackageReference>
instead of <PackageReference>
. This ensures certain packages are included in all projects without manually adding them to each .csproj
:
<Project>
<ItemGroup>
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0" />
</ItemGroup>
</Project>
Global package references are added to the PackageReference item group with the following metadata:
IncludeAssets="Runtime;Build;Native;contentFiles;Analyzers"
This ensures that the package is only used as a development dependency and prevents any compile-time assembly references.
PrivateAssets="All"
This prevents global package references from being picked up by downstream dependencies.
This is particularly useful for analyzers, logging frameworks, or shared utilities. It ensures the package is used only during development and is not referenced at compile-time or exposed to downstream consumers, reducing duplication and enforcing standard tooling.
Best Practices โ
Place
Directory.Packages.props
at the solution root to apply it broadly.Avoid version duplication across other
.csproj
files.Commit the centralized file to version control.
Use clear versioning policies, such as pinning to stable releases.
Use
dotnet list package --outdated
to track outdated packages centrally.Test builds after every version bump.
Conclusion ๐งพ
Centralized Package Management in .NET Core simplifies how you handle dependencies across multiple projects. It reduces maintenance costs, enforces consistency, and streamlines development workflows. By moving version declarations to a single file, teams can spend less time managing dependencies and more time building features.
References ๐
Subscribe to my newsletter
Read articles from Sang Au directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Sang Au
Sang Au
That's me! Daddy, Husband, Coder, Photographer