掌握在 .NET 中使用 NuGet 包
Source: Dev.to
什么是真正的 NuGet?
想象 NuGet 是 .NET 版的 Amazon 或 Mercado Libre。你不需要自己制造每个螺丝钉,而是从商店里买来。
- Package(包): 你购买的产品(Newtonsoft.Json、EntityFramework)。
- Source(Feed,源): 存放这些包的仓库或商店。
我的包会去哪里?
很多开发者以为执行 dotnet restore 时,DLL 会 神奇 地出现在项目里。了解真实的流程非常重要,才能知道背后到底发生了什么:
Request(请求): 你的项目(.csproj)请求 Newtonsoft.Json v13.0.1。
Restore(还原): NuGet 在已配置的 Sources 中查找。
Global Packages Folder(全局包文件夹): NuGet 将包下载并解压到用户的全局文件夹(Windows 通常是 %userprofile%\.nuget\packages,Linux/Mac 是 ~/.nuget/packages)。不会保存在你的项目内部。
Build(构建): 编译时,.NET 会从该全局文件夹读取 DLL,并复制到 bin/Debug 或 bin/Release 目录。

这有什么意义? 因为它可以节省空间。如果有 10 个项目使用同一个库,磁盘上只会下载一次。
配置层级
在写任何配置之前,你必须了解 NuGet 的“思考方式”。执行 restore 时,它不仅查看你的项目,还会按照严格的层级级联合并配置:
- 机器级(Machine Level): 这个文件往往根本不存在或是空的,除非管理员手动放置。
- 用户级(User Level): 最常见的 (
%appdata%\NuGet\NuGet.Config),随着时间会积累大量 “数字垃圾”。 - 目录级(递归,Directory Level): NuGet 会在解决方案所在文件夹查找…如果没有找到,就向上到父文件夹,再到祖父文件夹,直至磁盘根目录。
“在我的机器上能跑” 的根源 🤷♂️

这就是著名的 meme 的来源。假设你在 用户级 配置了一个私有 feed,因为你在公司里有多个项目要用。你新建一个项目,编译完美。你的同事克隆仓库后尝试编译,却报错。
为什么? 因为你的项目依赖了只存在于你本地用户配置中的 不可见 配置。对同事来说,这个 feed 并不存在。
检测实际使用的配置
在解决方案根目录执行:
dotnet nuget list source
该命令会显示 NuGet 在该目录实际使用的、合并后的所有源列表。如果看到陌生的路径或已下线的服务器,那就是继承导致的问题。
NuGet.config
在解决方案根目录显式创建 NuGet.config 文件可以打断有害的继承链,确保项目对所有人都可用。
elimina toda la "basura" heredada del usuario o máquina -->
这样配置的好处
- 🛡 隔离: 使用 “ 可以保护项目不受全局破损配置的影响。
- 🛠 独立性: 新开发者只需要
git clone再dotnet restore。 - 🔄 CI/CD 友好: 流水线明确知道去哪里找依赖,不会出现奇怪的步骤。
优先级的混乱
如果你同时配置了 nuget.org 和 GitHubFeed,且两者都包含名为 Newtonsoft.Json 的包,究竟会下载哪个?

NuGet 采用 “先响应者” 的策略,这带来两个风险:
- 性能: 在找到私有包之前,会先在公共仓库里搜索。
- 安全: 攻击者可能在
nuget.org上上传一个与内部包同名的恶意包(例如MiBanco.Core),你的项目可能在不知情的情况下下载到攻击者的包。 🚨
