Streamlining Your NuGet Package Versions

May 26, 2022#Software Development
Article
Author image.

Kyle McMaster, Senior Consultant

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.

NuGet Packages For Solution

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.

References


Copyright © 2022 NimblePros - All Rights Reserved