.NET Tools How-To's Rider

NuGet Central Package Management Comes To JetBrains Rider

We’re introducing one of the .NET ecosystem’s newest features in JetBrains Rider 2022.3 EAP, Central Package Management (CPM). CPM allows developers to manage NuGet dependencies in a .NET solution from a central location. This can simplify the upgrade process of dependencies and keep your solutions from experiencing dependency drift. 

Introduced recently to the NuGet ecosystem, developers targeting .NET SDK 6.0.300+ or .NET 7.0 versions can begin to migrate to central package management. In this post, we’ll see how to change your projects to take advantage of this new feature and discuss some current issues you may want to address.

Getting Started

Before moving to Central Package Management (CPM), you’ll need to ensure you have a .NET SDK that supports NuGet 6.2 or later. Any previous tooling will not work and will ignore any solution-specific configuration.

Once you’ve started a new project, you’ll need to create a new Directory.packages.props file at the root of your solution. This file will include all your solution’s centralized packages and the current version of any specific package. These .props files add additional information to your MSBuild configuration. The easiest way to add this new file with Rider is to switch from the solution explorer view to the file system view, then use the context menu on the root folder and add the new file.

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <!--    add your PackageVersion nodes here -->
  </ItemGroup>
</Project>

Once you’ve added the Directory.packages.props file to your solution, JetBrains Rider will start managing your solution dependencies centrally, updating the central file appropriately. The NuGet UI within the IDE will function the same as it would have with individual projects. From the NuGet UI, you can add additional dependencies, upgrade, downgrade, or remove unwanted packages as you’re used to seeing.

After installing a package into multiple projects, you’ll notice that version numbers are described for each project in the solution, which is now dictated by the CPM file.

Centralized Package Management in JetBrain Rider’s NuGet Tool Window

Now let’s see what happens to your project files when you perform basic NuGet actions.

When adding a NuGet package to the solution, you’ll notice a new PackageVersion element in your Directory.package.props is similar to the elements commonly found in a project file. Here’s the package reference to Spectre.Console we just added, which the tooling will use across multiple projects in the solution.

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Spectre.Console" Version="0.45.1-preview.0.16" />
  </ItemGroup>
</Project>

Simultaneously, when looking at our project files, you’ll notice a PackageReference element, but this time it’s lacking the Version attribute. A feature of CPM is that all versions are typically managed centrally.

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net7.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>
    
    <ItemGroup>
        <PackageReference Include="Spectre.Console" />
    </ItemGroup>

</Project>

Congratulations! Following these straightforward steps, you’re now using centralized package management in your .NET Solution.

Central Package Management Tips and Caveats

There are a few things you should know about CPM and issues you may encounter when switching to this method of package management.

The first issue I encountered was a warning about package sources. For example, you may see the following warning when restoring packages.

Warnings about package sources

warning NU1507: There are 2 package sources defined in your configuration. 
When using central package management, please map your package sources with 
package source mapping (https://aka.ms/nuget-package-source-mapping) 
or specify a single package source.

The additional source triggering the warning is a known issue with the library-packs directory included implicitly by dotnet to the restoration process. To avoid this issue, add a NoWarn element to your Directory.packages.props file.

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
    <NoWarn>NU1507</NoWarn>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Spectre.Console" Version="0.45.1-preview.0.16" />
  </ItemGroup>
</Project>

You should see the warning disappear for now. However, the .NET team is aware of this issue and will address it in the future, so you won’t need to suppress this warning.

Reduce package sources with mappings

If you see a number larger than “2” in the previous warning, you may want to consider Package Source Mapping.  Package source mapping forces the NuGet process to limit the source of a NuGet package to explicitly white-listed sources. Mapping sources prevents potential supply-chain attacks and keeps an element of predictability in your builds.

Here’s a starting nuget.config file you can use at the root of your solution directory.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- Define the package sources, nuget.org and contoso.com. -->
    <!-- `clear` ensures no additional sources are inherited from another config file. -->
    <packageSources>
        <clear/>
        <!-- `key` can be any identifier for your source. -->
        <add key="nuget.org" value="https://api.nuget.org/v3/index.json"/>
    </packageSources>
    <!-- Define mappings by adding package patterns beneath the target source. -->
    <!-- Contoso.* packages and NuGet.Common will be restored from contoso.com, everything else from nuget.org. -->
    <packageSourceMapping>
        <!-- key value for <packageSource> should match key values from <packageSources> element -->
        <packageSource key="nuget.org">
            <package pattern="*"/>
        </packageSource>
    </packageSourceMapping>
</configuration>

Back to Project-level Dependency Management

Sometimes, it makes sense to manage a package dependency separate from the rest of the solution. There are two techniques to manage dependencies at the project level.

The first way is to use the VersionOverride attribute on the PackageReference element.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Spectre.Console" VersionOverride="0.45.1-preview.0.15" />
  </ItemGroup>
<Project>

The other approach is to opt out entirely using the ManagePackageVersionsCentrally element in your project files.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>	
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
     <PackageReference Include="Spectre.Console" Version="0.45.1-preview.0.16" />
  </ItemGroup>
<Project>

Now you’re back to managing dependencies at the project level.

Conclusion

Central Package Management is an evolving feature of the .NET technology stack, and as it improves, it should make managing solutions easier moving forward. With the addition of CPM in Rider, developers should have more control over their dependencies.

Try out CPM support in the latest EAP of JetBrains Rider, and let us know how we can improve the experience. 

Thanks for reading, and leave any comments and questions below.

Discover more