Earlier this year Microsoft announced a new way to manage dependencies in .NET applications called Central Package Management. This new way of managing dependencies separates declaration of dependencies to project files and versioning of dependencies to a file named Directory.Packages.props
. Traditionally, NuGet package dependencies were managed in XML that spanned across many files in multi-project solutions. In modern .NET applications that utilize the SDK project style, the format <PackageReference Include="Ardalis.Specification" Version="Version="6.0.1" />
is typically used. This tells MSBuild a project references version 6.0.1 of the Ardalis.Specification NuGet package. This way of managing dependencies can be cumbersome to read and often more difficult to resolve merge conflicts when using source control management tooling like Git. In this post, we’ll apply central package management to the Clean Architecture template project and discuss some of the benefits of using it.
Setting up Central Package Management
Central package management can be easily leveraged by modern .NET Core applications. To get started, create a Directory.Packages.props
in the same folder as the Clean.Architecture.sln
at the root of the repository. This file is where the version information of NuGet packages that are used by the application will be defined. This file will also contain the following PropertyGroup that tells MSBuild to use central package management for dependencies.
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
Once the file has been created, each package that is used by the application should be added to the Directory.Packages.props
file. The syntax used to add a package to the file should feel familiar as it is similar to the format used by projects in the solution but the node is different (PackageReferences
vs PackageVersion
). This makes sense given that the file is supplying the versions of the packages needed and not specifying the references to the packages themselves. Here is a brief example showing the Directory.Packages.props
file with the package dependencies from the Clean.Architecture.Core
project.
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.GuardClauses" Version="4.0.1" />
<PackageVersion Include="Ardalis.Result" Version="3.1.2" />
<PackageVersion Include="Ardalis.SmartEnum" Version="2.1.0" />
<PackageVersion Include="Ardalis.Specification" Version="6.0.1" />
<PackageVersion Include="Autofac" Version="6.3.0" />
<PackageVersion Include="MediatR" Version="10.0.1" />
</ItemGroup>
</Project>
Once a package has been added to the Directory.Packages.props
file, the PackageReference
in the project file can then be updated to omit the package version. Here is the updated Clean.Architecture.Core.csproj
file with the versions removed.
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Microsoft.Build.CentralPackageVersions" Version="2.1.3" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" />
<PackageReference Include="Ardalis.Result" />
<PackageReference Include="Ardalis.SmartEnum" />
<PackageReference Include="Ardalis.Specification" />
<PackageReference Include="Autofac" />
<PackageReference Include="MediatR" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Clean.Architecture.SharedKernel\Clean.Architecture.SharedKernel.csproj" />
</ItemGroup>
</Project>
Benefits of Central Package Management
This single file for managing versions of dependencies makes maintenance easier for developers. With the version attribute removed from each PackageReference
, the project file is easier to read with less clutter. Now, the PackageReference
section of a project file only changes when dependencies are added or removed compared to the previous format where the PackageReference
section was updated with each new version of each package. This lowers the likelihood of errors leading to less consolidation of packages and reduces the surface area for merge conflicts over time.
Final Steps
Once each project in the solution has been configured to use central package management, I ended up with a Directory.Packages.props
file that looks like the following.
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.ApiEndpoints" Version="4.0.1" />
<PackageVersion Include="Ardalis.GuardClauses" Version="4.0.1" />
<PackageVersion Include="Ardalis.HttpClientTestExtensions" Version="1.0.0" />
<PackageVersion Include="Ardalis.ListStartupServices" Version="1.1.3" />
<PackageVersion Include="Ardalis.Result" Version="3.1.2" />
<PackageVersion Include="Ardalis.Result.AspNetCore" Version="3.1.2" />
<PackageVersion Include="Ardalis.SmartEnum" Version="2.1.0" />
<PackageVersion Include="Ardalis.Specification" Version="6.0.1" />
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="6.0.1" />
<PackageVersion Include="Autofac" Version="6.3.0" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
<PackageVersion Include="coverlet.collector" Version="3.1.2" />
<PackageVersion Include="MediatR" Version="10.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.5" />
<PackageVersion Include="Moq" Version="4.18.1" />
<PackageVersion Include="NETStandard.Library" Version="2.0.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
<PackageVersion Include="ReportGenerator" Version="5.1.9" />
<PackageVersion Include="Serilog.AspNetCore" Version="5.0.0" />
<PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="3.1.0" />
<PackageVersion Include="SQLite" Version="3.13.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.3.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="6.3.1" />
<PackageVersion Include="xunit" Version="2.4.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
</ItemGroup>
</Project>
A quick inspection of Manage NuGet Packages For Solution
in Visual Studio shows that all the packages are installed with the correct versions.
Just like that you can manage your dependencies in a manner that is more focused on the concerns of the file type. You can get started with central package management today using NuGet 6.2, Visual Studio 2022 17.2, .NET SDK 6.0.300 and 7.0.0-preview.4 (or higher). For more information on the new features and more advanced configuration options, be sure to checkout the links to the announcement and Microsoft Docs pages in the references below.