Search code examples
visual-studiopackagenugetversion

NuGet package, define common minumum version, allow higher?


I want to centrally specify minimum NuGet package versions, to exclude vulnerable, transitive dependencies, but also allow later versions, if required as transitive dependencies by other packages.

I am trying the rather new Central Package Management (CPM) in Visual Studio 2022, using a Directory.Packages.props file on solution level: https://learn.microsoft.com/en-us/nuget/concepts/dependency-resolution https://devblogs.microsoft.com/nuget/introducing-central-package-management/

Alternatively, I could use Directory.Build.props or the older packages.config.

In packages.config it would look like:

<package id="Newtonsoft.Json" version="13.0.1" allowedVersions="13.0.0" />

with the allowedVersions="13.0.0" allowing everything 13.0.0 or greater.

Behavior, for NuGet packages referenced in projects, should be:

  1. Package JsonA has transitive Newtonsoft.Json 12.0.1 => gets 13.0.1
  2. Project requires Newtonsoft.Json 13.0.3 (update, new version in project)
  3. Package JsonB requires transitive Newtonsoft.Json 13.0.3 => project shall get update to 13.0.3

Case 3 fails when I define

<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />

in one of the Directory.x.props files, I get NU1605 Detected Package Downgrade error (or warning, depending on error config). But package JsonB (case 3) shall be using version 13.0.3, without specifying the transitive dependency in the project explicitly.

Is this possible, and if, how?


Solution

  • I finally found it, as far as the minimum version, using Central Package Management CPM, as linked above (VS2022 17.2+, NuGet 6.2+).

    For Newtonsoft.Json, it looks like this. In the Directory.Packages.props file (new with CPM):

        <PropertyGroup>
            <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
            <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
        </PropertyGroup>
    

    TransitivePinning makes NuGet pull the declared version for transitive dependencies, even if they declare another version.

    Then the PackageVersion element. This only specifies the version, does not add the package to all projects. Actually, a PackageVersion declaration may not be used at all.

    <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />

    In the project file, without Version attribute:

    <PackageReference Include="Newtonsoft.Json" />

    Other versions can be referenced on project level via VersionOverride attribute.

    An automatic upgrade to later versions, e.g. when a dependency requires Newtonsoft.Json 13.0.4, does not work yet and has to be edited manually. PackageVersion Version attributes may, as of now, not contain floating versions (* wildcard, range notation).

    Difference to old Directory.Build.props:

    That one had only the PackageReference element; adding one on solution level would create a dependency in every NuGet package created by the solution, even where not needed.