中央包管理(CPM)

依赖项管理是 NuGet 的核心功能。 管理单个项目的依赖项很容易。 随着多项目解决方案在规模和复杂性上逐渐增加,管理其依赖项可能会变得困难。 在管理许多不同项目的常见依赖项的情况下,可以利用 NuGet 的中央包管理(CPM)功能从单个位置轻松完成所有这些作。

从历史上看,NuGet 包依赖项已在以下两个位置之一进行管理:

  • packages.config - 旧项目类型中使用的 XML 文件,用于维护项目引用的包列表。
  • <PackageReference /> - MSBuild 项目中使用的 XML 元素定义 NuGet 包依赖项。

NuGet 6.2开始,可以通过添加 Directory.Packages.props 文件和 MSBuild 属性集中管理项目中的依赖项。

此功能在所有 NuGet 集成工具中都可用,从以下版本开始。

较旧的工具将忽略中央包管理配置和功能。 若要充分利用此功能,请确保所有生成环境都使用最新的兼容工具版本。

只要使用兼容的工具,中央包管理将应用于所有基于 <PackageReference>的 MSBuild 项目(包括 旧版 CSPROJ)。

启用中央软件包管理

若要开始中央包管理,必须在存储库的根目录中创建 Directory.Packages.props 文件,并将 MSBuild 属性 ManagePackageVersionsCentrally 设置为 true

可以手动创建它,也可以使用 dotnet CLI:

dotnet new packagesprops

然后,在内部使用定义包 ID 和版本的 <PackageVersion /> 元素来定义项目所需的各个相应包版本。

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
  </ItemGroup>
</Project>

然后,为每个项目定义一个 <PackageReference />,但省略 Version 属性,因为版本信息将从相应的 <PackageVersion /> 项中获取。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" />
  </ItemGroup>
</Project>

现在你正在使用中央包管理,并在集中位置管理版本!

中央软件包管理规则

Directory.Packages.props 文件包含许多有关它位于存储库目录及其上下文中的规则。 为简单起见,只评估给定项目的一个 Directory.Packages.props 文件。

这意味着,如果存储库中有多个 Directory.Packages.props 文件,则会评估离项目目录最近的文件。 这样就可以在存储库的各个级别进行额外的控制。

下面是一个示例,请考虑以下存储库结构:

Repository
 |-- Directory.Packages.props
 |-- Solution1
     |-- Directory.Packages.props
     |-- Project1
 |-- Solution2
     |-- Project2
  • Project1 将评估 Repository\Solution1\ 目录中的 Directory.Packages.props 文件,如果需要,它必须手动导入下一个文件。
    <Project>
      <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Packages.props, $(MSBuildThisFileDirectory)..))" />
      <ItemGroup>
        <PackageVersion Update="Newtonsoft.Json" Version="12.0.1" />
      </ItemGroup>
    </Project>
    
  • Project2 将评估 Repository\ 目录中的 Directory.Packages.props 文件。

注意: MSBuild 不会自动为你导入每个 Directory.Packages.props,仅导入离项目最近的第一个。 如果有多个 Directory.Packages.props,您必须手动导入父级,而根 Directory.Packages.props 则不需要手动导入。

入门

若要完全载入存储库,请考虑执行以下步骤:

  1. 在名为 Directory.Packages.props 的存储库的根目录中创建一个新文件,声明集中定义的包版本,并将 MSBuild 属性 ManagePackageVersionsCentrally 设置为 true
  2. Directory.Packages.props中声明 <PackageVersion /> 项目。
  3. 在你的项目文件中声明没有 Version 属性的 <PackageReference /> 项目。

有关中央包管理的概念,请参阅我们的 示例存储库

传递性锁定

即使没有显式的顶级 <PackageReference />,您也可以通过选择加入一种称为传递锁定的功能来自动覆盖可传递包的版本。 这将在必要时以隐式方式将可传递依赖项提升为顶级依赖项。 请注意,在传递依赖关系固定一个软件包时允许降级。 如果尝试将包固定到低于依赖项请求的版本,还原将引发 NU1109 错误。

可以通过将 MSBuild 属性 CentralPackageTransitivePinningEnabled 设置为在项目或 Directory.Packages.propsDirectory.Build.props 导入文件中 true 来启用此功能:

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

可传递固定和打包

当一个包被传递性固定时,您的项目所使用的版本号高于依赖项请求的版本号。 如果从项目创建包,为了确保包能正常工作,NuGet 会将可传递固定的依赖项提升为 nuspec 中的显式依赖项。

在以下示例中,PackageA 1.0.0 依赖于 PackageB 1.0.0

<Project>
  <ItemGroup>
    <PackageVersion Include="PackageA" Version="1.0.0" />
    <PackageVersion Include="PackageB" Version="2.0.0" />
  </ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="PackageA" />
  </ItemGroup>
</Project>

使用 pack 命令创建包时,这两个包将显示在依赖项组中。

      <group targetFramework="net6.0">
        <dependency id="PackageA" version="1.0.0" exclude="Build,Analyzers" />
        <dependency id="PackageB" version="2.0.0" exclude="Build,Analyzers" />
      </group>

因此,在创建库时,应仔细评估使用传递性锁定,因为这可能会导致你意想不到的依赖项。

覆盖包版本

可以使用 <PackageReference /> 项上的 VersionOverride 属性替代单个包版本。 这会覆盖集中定义的任何 <PackageVersion />

<Project>
  <ItemGroup>
    <PackageVersion Include="PackageA" Version="1.0.0" />
    <PackageVersion Include="PackageB" Version="2.0.0" />
  </ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="PackageA" VersionOverride="3.0.0" />
  </ItemGroup>
</Project>

可以通过将 MSBuild 属性 CentralPackageVersionOverrideEnabled 设置为在项目或 Directory.Packages.propsDirectory.Build.props 导入文件中 false 来禁用此功能:

<PropertyGroup>
  <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup>

禁用此功能后,在任何 <PackageReference /> 项上指定 VersionOverride 将导致还原时出错,指示该功能已禁用。

禁用中央包管理

如果要禁用任何特定项目的中央包管理,可以通过将 MSBuild 属性 ManagePackageVersionsCentrally 设置为 false来禁用它:

<PropertyGroup>
  <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>

全局包引用

注意

此功能仅适用于 Visual Studio 2022 17.4 或更高版本、.NET SDK 7.0.100.preview7 或更高版本以及 NuGet 6.4 或更高版本。

全局包引用用于指定某个包将由存储库中的每个项目使用。 这些包包括用于版本控制、扩展构建功能的包,以及所有项目所需的其他任何包。 全局包引用将添加到具有以下元数据的 PackageReference 项组:

  • IncludeAssets="Runtime;Build;Native;contentFiles;Analyzers"
    这可确保包仅用作开发依赖项,并阻止任何编译时程序集引用。
  • PrivateAssets="All"
    这可以防止全局包引用被下游依赖项获取。

应将 GlobalPackageReference 项放在 Directory.Packages.props 中,以供存储库中的每个项目使用。

<Project>
  <ItemGroup>
    <GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.5.109" />
  </ItemGroup>
</Project>

使用多个包源时的警告

使用中央包管理时,如果配置中定义了多个包源,你将看到 NU1507 警告。 若要解决此警告,请使用 包源映射 映射包源或指定单个包源。

There are 3 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.

注意

中央包管理正在积极开发中。 我们感谢您试用它并在 NuGet/Home提供任何反馈。