visual studio/nuget 如何决定包的版本 download/exact?

How does visual studio/nuget decides what version(s) of the package to download/exact?

这是一个 C# 项目。我知道 nuget 向 packages.config 咨询 download/extract 包的信息。但是我注意到它为不同的 .net 平台下载了多个版本。例如,在 packages.config:

<package id="log4net" version="1.2.11" targetFramework="net461"/>

nuget下载net10到net40的所有版本。另外,.csproj文件中指定的位置与物理位置不一致。

在上面的示例中,.csproj 中的位置类似于:

src/packages/lognet.1.2.11/lib/log4net.dll

但是,dll 的物理位置是:

src/packages/log4net1.2.11/lib/netXX-full/log4net.dll

XX可以从10到40,前面说了下载解压多个版本的log4net

由于不一致,项目注定要失败,引用 package not found 错误。

我该如何解决这个问题?

仅供参考:几天前我问了一个相关但不同的问题。

“开门见山”回答:您不必担心。当您在项目中安装或升级包时,NuGet 会告诉项目系统将什么放入 csproj。请注意,这与恢复包不同。在 packages.config 项目中安装包意味着 Visual Studio 将同时修改 packages.config 文件和 csproj 文件。当您的计算机上没有 nuget 包时,当 NuGet 下载并解压缩它时,这称为恢复,而不是安装。在上一个问题中,您说您克隆了其他人的存储库并试图构建它。您可以问他们 how/why csproj 与 nuget.org 上的 log4net 包不匹配。或者在 Visual Studio 中,按 ctrl-q 进行搜索,键入“包管理器控制台”并 select。初始化后,在“默认项目”下拉列表中 select 带有错误 log4net 引用的项目,然后键入 Update-Package -reinstall。这应该解决它。或者,您可以右键单击解决方案资源管理器中的项目,select 管理 NuGet 包,卸载 log4net,然后重新安装。您还可以考虑 migrating from packages.config to PackageReference,它没有 packages.config 存在的提示路径问题。

长而详细的回答:使用NuGet时,“版本”一词通常是指包版本,在本例中为1.2.11。查看 package page on nuget.org,在版本历史记录下我们可以看到其他版本,如 1.2.10、2.0.0、2.0.1 直到 2.0.8。除非你有多个项目,每个项目都引用不同版本的包,否则 NuGet 不会下载多个版本。

.NET 有不同版本的运行时,其中一些与其他版本不兼容,但这些称为目标框架名字对象 (TFM),尽管不在 Microsoft 工作的人倾向于将它们称为目标框架之类的。它类似于其他托管运行时或脚本语言,如 Java、Python、PHP 等,但可能更复杂,因为过去创建了大量 TFM,主要用于具有不同功能的不同移动设备。此外,Windows 运行时分为 3 个“系列”,其中 TFM 向后兼容,但仅限于同一个“系列”。它使用的 Nuget Client has a complex mapping of compatible TFMs select 与您的项目的 NuGet 包中最接近的兼容 TFM。

一个包可能包含不同 TFM 的多个 dll 的另一个原因是因为他们希望在较新的 TFM 中使用较新的 API,同时仍然允许针对较旧的 TFM 的项目在没有新 API 的情况下使用该包。例如,虽然 net45、net46、net47 和即将推出的 net48 都与 net40 兼容,但 net40 没有 async-await。因此,一个包可能有一个 net40 dll,因此以 net40 为目标的项目仍然可以使用该包,但该包还可以包含一个带有额外异步 API 的 net45 dll。 net1x、net 2x 和 net3x 太旧了(net40 和 net45 也是如此)以至于软件包很少再为这些旧版本提供 dll。然而,log4net 1.2.11 于 2011 年发布,当时这些较旧的 TFM 可能仍然很常见,值得为其提供兼容性。

因此,您在log4net 包的lib 文件夹中看到的不是不同版本的log4net,它实际上是同一版本的log4net,但针对不同的TFM 进行了编译。这增加了可以使用该包的项目数量。

您正在使用的项目具有不存在的 HintPath 的原因有多种。鉴于您目前提供的信息信息,这听起来像是有人手动编辑了 csproj,或者有人创建了他们自己的 log4net v1.2.11 nuget 包,与 nuget.org。 NuGet 包被设计为不可变的,这意味着下载具有特定包 ID(名称)和包版本的 nupkg 应该与来自任何其他 NuGet 提要的具有相同包 ID + 版本的所有其他 nupkg 相同。如果不是,当 NuGet 从与安装程序包的人不同的来源恢复程序包时,您可能会遇到构建错误。

最后,带有 packages.config 的 NuGet 不是为手动编辑而设计的。您不应自行编辑和保存 packages.config 文件或 csproj 中的 References 或 HintPath。使用 NuGet 包管理器 UI 或包管理器控制台安装、卸载和升级包。 NuGet 客户端中有很多逻辑,不仅仅是资产 selection,如果包使用特定功能,你会出错。

2017 年 Visual Studio,NuGet 添加了一种引用包的新方法,称为 PackageReference。您可能需要转到“工具”->“选项”,找到“NuGet 包管理器”->“常规”,然后更改默认包管理格式或启用“首次安装包时允许格式 selection”。也应该可以右键单击许多项目类型并选择迁移到 PackageReference,但尚未为所有项目类型启用此选项(主要是 ASP.NET)。 PackageReference 不支持 packages.config 支持的某些功能,并且某些项目类型也不支持 PackageReference。但在最常见的情况下它是受支持的,如果你可以使用它,它有几个优点,包括不再在你的 csproj 中放置提示路径并避免你当前遇到的特定问题(恢复时 NuGet 写入文件 obj\project.assets.json 其中包含编译器使用的 selected 资产的路径,这意味着如果您更改源代码存储库结构,只需执行 NuGet 恢复即可修复它,而使用 packages.config 您可能需要重新安装每个项目中的每个包)。使用 .NET Core 引入的新 SDK 样式的项目根本不支持 packages.config,不管它值多少钱。

哇,我闲逛的时间比我预期的要长得多。我希望它对你有所帮助,而不是让你感到困惑。