一、问题背景
最近需要将一个 WPF 工程从联网开发机完整迁移到一台完全物理隔离的离线设备上。本以为复制整个项目文件夹和 NuGet 包缓存就能搞定,结果在离线电脑上编译时遇到了接二连三的"惊喜":
-
找不到资产文件 project.assets.json,请运行 NuGet 包还原 -
检测到包降级: Microsoft.Extensions.Configuration.Abstractions 从 9.0.8 降级到 9.0.0 -
Could not find a part of the path ... microsoft.win32.systemevents.4.7.0.nupkg
历经多次折腾后,终于让项目在离线环境下完美编译运行。现将完整解决思路和最终方案记录于此,希望能帮助到遇到类似问题的朋友。
二、为什么简单的"复制粘贴"会失败?
在理解解决方案之前,先搞清楚三个关键点:
| 现象 | 根本原因 |
|---|---|
project.assets.json 缺失 |
该文件由 dotnet restore 动态生成,默认不被 git 收录,拷贝时极易遗漏 |
| 包降级警告 (NU1605) | NuGet 依赖解析遵循就近原则。如果项目直接引用 A v1.0,而另一个间接依赖要求 A ≥ v2.0,NuGet 会选择 v1.0 并发出警告 |
找不到具体的 .nupkg |
离线包源不完整 ------ 要么拷贝时漏了某些包,要么包的文件夹结构不符合 NuGet 预期 |
简单复制
bin和obj是行不通的,因为那些是编译输出,换了环境必须重新还原和生成。
三、最终有效方案对比
经过实验,下面两种方案都能在离线环境下达成目标。方案一更加推荐(一劳永逸,适合后续继续在离线电脑上开发调试)。
✅ 方案一:使用 packages.lock.json 锁定依赖(推荐)
核心思想:告诉 NuGet "不要分析,就按这个清单恢复"。
步骤 1:在联网电脑上启用锁文件
编辑你的 .csproj 项目文件,在任意 <PropertyGroup> 中添加:
xml
复制
下载
运行
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
步骤 2:生成完整的离线包源并锁定版本
在解决方案目录打开终端,执行:
bash
复制
下载
# 将所有依赖包下载到 ./offline_packages 文件夹,并生成 packages.lock.json
dotnet restore --packages ./offline_packages --use-lock-file
执行成功后,项目目录下会出现:
-
offline_packages/文件夹(内含所有.nupkg) -
packages.lock.json文件(记录了精确版本树)
步骤 3:将以下内容一并拷贝到离线电脑
text
复制
下载
项目根目录/
├── .sln 解决方案文件
├── 所有 .csproj 项目文件
├── packages.lock.json ← 必须带上
├── offline_packages/ ← 整个文件夹
├── 源代码文件夹
└── (可选) NuGet.config ← 见下文
步骤 4:在离线电脑上配置本地包源(二选一)
方法 A:通过 Visual Studio 界面
工具 → NuGet 包管理器 → 程序包管理器设置 → 程序包源
-
取消勾选
nuget.org或其他在线源 -
点击 [+] 添加新源,路径指向
offline_packages文件夹
方法 B:在项目根目录新建 NuGet.config 文件(推荐,可随工程迁移)
xml
复制
下载
运行
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear /> <!-- 清除所有默认源 -->
<add key="local-offline" value="./offline_packages" />
</packageSources>
</configuration>
步骤 5:在离线电脑上执行锁模式还原
打开终端(或 VS 的开发人员命令提示符),进入解决方案目录:
bash
复制
下载
dotnet restore --locked-mode --packages ./offline_packages
--locked-mode会强制要求packages.lock.json存在且完全匹配,不会重新解析版本,因此彻底避免了降级警告。
如果一切正常,现在就可以正常 生成解决方案 了。
✅ 方案二:发布为"独立"(Self-contained) 可执行文件
如果你不需要 在离线电脑上修改源码后重新编译,仅仅需要运行程序,这是最省事的办法。
在联网电脑上:
-
右键项目 → 发布
-
目标选择 文件夹
-
设置:
-
部署模式 = 独立
-
目标运行时 =
win-x64(根据离线电脑系统选择) -
勾选 生成单个文件(可选,方便拷贝)
-
-
点击 发布
发布完成后,将 publish 文件夹整个拷贝到离线电脑,直接双击 .exe 即可运行,不需要安装 .NET 运行时,也完全不需要处理任何 NuGet 问题。
缺点:发布文件夹体积较大(包含了运行时和所有依赖),且无法修改源码重新编译。
四、踩坑记录 & 避坑建议
| 问题现象 | 解决办法 |
|---|---|
还原时仍提示缺少 xxx.nupkg |
回到联网电脑,用 dotnet restore --packages ./offline_packages 重新下载完整依赖,然后重新拷贝整个文件夹 |
| 明明所有包都拷贝了,还是降级 | NuGet 默认行为就是"自动协商版本"。必须用 packages.lock.json + --locked-mode 或使用中央包管理(CPM)强制固定版本 |
dotnet restore 卡住或超时 |
检查是否误勾选了在线源,或者 NuGet.config 中没有生效。可以用 --source 参数直接指定:dotnet restore --source ./offline_packages |
| 锁文件生成失败 | 确保项目文件已添加 <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>,然后执行 dotnet restore --use-lock-file |
五、总结
| 你的需求 | 推荐方案 |
|---|---|
| 离线电脑上需要修改代码并重新编译 | 方案一(锁定依赖 + 本地包源) |
| 离线电脑上仅运行,不改代码 | 方案二(独立发布) |
最终让我成功的是 方案一 ,它绕过了 NuGet 复杂的版本协商机制,让离线环境下的依赖恢复变得确定、可重复。如果你的项目也遇到了类似的 NU1605 降级警告,不妨试试这套"锁文件 + 本地源"的组合拳。
附录:常用命令速查
bash
复制
下载
# 联网环境下下载依赖并生成锁文件
dotnet restore --packages ./offline_packages --use-lock-file
# 离线环境下使用锁文件还原(不联网)
dotnet restore --locked-mode --packages ./offline_packages
# 清理所有缓存(谨慎使用)
dotnet nuget locals all --clear
# 查看当前配置的包源
dotnet nuget list source