管理 .NET 项目中的依赖项更新

已完成

你迟早需要更新到新版本的库。 可能某个函数被标记为已弃用,或者你正在使用的包的更高版本中可能存在新功能。

尝试更新库之前,应考虑以下注意事项:

  • 更新类型:哪种更新类型可用? 是小型 bug 修复吗? 是添加了你需要的新功能吗? 会中断你的代码吗? 可以使用称为“语义化版本控制”的系统来传达更新类型。 库版本号的表示方法可向开发人员传达其正在处理的更新类型。
  • 项目是否正确配置:可配置 .NET 项目,以仅获取所需的更新类型。 仅在特定类型的更新可用时,才会执行更新。 建议使用此方法,因为这样可以避免意外的更新。
  • 安全问题:随着时间推移,管理项目依赖项需要注意可能发生的问题。 例如,检测到漏洞时会出现问题。 理想情况下,将发布你可以下载的修补程序。 .NET Core 工具可帮助你在库上运行审核,以找出是否有应更新的包。 它还可帮助你采取适当的措施来解决问题。

使用语义化版本控制

有一个称为语义化版本控制的行业标准,即如何表达你或其他开发人员向库引入的更改类型。 语义化版本控制的工作原理是确保包具有版本号,并且该版本号划分为以下部分:

  • 主版本:最左边的数字。 例如 1 中的 1.0.0。 此数字发生更改意味着代码可能出现重大变更。 可能需要重写部分代码。
  • 次要版本:中间的数字。 例如 2 中的 1.2.0。 此数字发生更改意味着添加了新功能。 你的代码仍可正常工作。 接受更新通常是安全的做法。
  • 修补程序版本:最右边的数字。 例如 3 中的 1.2.3。 此数字发生更改意味着应用了一个更改,修复了代码中应正常工作的内容。 接受更新应是安全的。

下表说明了每个版本类型的版本号如何更改:

类型 发生的更改
主版本 1.0.0 更改为 2.0.0
次要版本 1.1.1 更改为 1.2.0
修补程序版本 1.0.1 更改为 1.0.2

许多公司和开发人员都采用了此系统。 如果你打算发布包并将其推送到 NuGet 注册表,则应遵循语义化版本控制。 即使只是从 NuGet 注册表下载包,也可期望这些包遵循语义化版本控制。

对包进行更改可能会带来风险,例如 bug 可能会破坏业务。 一些风险可能需要你重写部分代码。 重写代码会花费时间和成本。

更新方法

作为 .NET 开发人员,你可以向 .NET 传达所需的更新行为。 从风险角度考虑更新。 以下是一些方法:

  • 主版本:我很乐意在最新主版本发布后立即更新到最新主版本。我接受可能需要更改我的代码这一事实。
  • 次要版本:我能接受添加新功能。 我不能接受代码中断。
  • 修补程序版本:我唯一能接受的更新是 bug 修复。

如果你正在管理一个新的或更小的 .NET 项目,你可以随意定义更新策略。 例如,你始终可以更新到最新版本。 对于较复杂的项目,还有其他细微差别,我们将在以后的模块中讨论。

通常,更新的依赖项越小,它拥有的依赖项就越少,并且更新过程可能会更简单。

配置项目文件以进行更新

添加一个或多个依赖项时,请考虑配置项目文件,以便在还原、生成或运行项目时获得可预测的行为。 你可传达要对包采取的方法。 NuGet 具有 版本范围浮动版本的概念。

版本范围是一种特殊表示法,可用于指示要解析的特定版本范围。

表示法 应用的规则 说明
1.0 x >= 1.0 最低版本(包含)
(1.0,) x > 1.0 最低版本(独占)
[1.0] x == 1.0 精确的版本匹配
(,1.0] x ≤ 1.0 最高版本(包含)
(,1.0) x < 1.0 最高版本(独占)
[1.0,2.0] 1.0 ≤ x ≤ 2.0 精确范围(包含)
(1.0,2.0) 1.0 < x < 2.0 精确范围(独占)
[1.0,2.0) 1.0 ≤ x < 2.0 混合了最低版本(包含)和最高版本(独占)
(1.0) 无效 无效

NuGet 还支持对数字的主要版本部分、次要版本部分、修补程序版本部分和预发布版本后缀部分使用可变版本表示法。 此表示法是一个星号 (*)。 例如,版本规范 6.0.* 表示“使用最新的 6.0.x 版本”。在另一个示例中,4.* 表示“使用最新的 4.x 版本”。使用可变版本可减少对项目文件的更改,同时能够及时了解依赖项的最新版本。

备注

建议安装特定版本,而不是使用任意可变表示法。 安装特定版本可确保生成可重复进行,除非明确请求更新依赖项。

使用可变版本时,NuGet 将解析与版本模式匹配的最新版包。 在下面的示例中,6.0.* 获取以 6.0 开头的包的最新版本。 该版本为 6.0.1。

请求浮动版本时,选择使用最新版本的示意图。

下面是可为主要版本、次要版本或修补程序版本配置的一些示例:

<!-- Accepts any version 6.1 and later. -->
<PackageReference Include="ExamplePackage" Version="6.1" />

<!-- Accepts any 6.x.y version. -->
<PackageReference Include="ExamplePackage" Version="6.*" />
<PackageReference Include="ExamplePackage" Version="[6,7)" />

<!-- Accepts any later version, but not including 4.1.3. Could be
     used to guarantee a dependency with a specific bug fix. -->
<PackageReference Include="ExamplePackage" Version="(4.1.3,)" />

<!-- Accepts any version earlier than 5.x, which might be used to prevent pulling in a later
     version of a dependency that changed its interface. However, we don't recommend this form because determining the earliest version can be difficult. -->
<PackageReference Include="ExamplePackage" Version="(,5.0)" />

<!-- Accepts any 1.x or 2.x version, but not 0.x or 3.x and later. -->
<PackageReference Include="ExamplePackage" Version="[1,3)" />

<!-- Accepts 1.3.2 up to 1.4.x, but not 1.5 and later. -->
<PackageReference Include="ExamplePackage" Version="[1.3.2,1.5)" />

查找和更新过时的包

dotnet list package --outdated 命令列出了已过时的包。 此命令可帮助你了解何时有更新版本的包可用。 以下是命令的典型输出:

Top-level Package      Requested   Resolved   Latest
> Humanizer            2.7.*       2.7.9      2.8.26

以下是输出中列名称的含义:

  • Requested:已指定的版本或版本范围。
  • Resolved:已为项目下载的实际版本与指定版本相匹配。
  • Latest:可从 NuGet 更新的最新版本。

建议的工作流是按以下顺序运行这些命令:

  1. 运行 dotnet list package --outdated。 此命令可列出所有已过时的包。 它通过 RequestedResolvedLatest 列提供信息。
  2. 运行 dotnet add package <package name>。 如果运行此命令,它将尝试更新到最新版本。 你可选择传入 --version=<version number/range>