NuGet 依赖管理发展历史(packages.config → PackageReference)

一、NuGet 是什么?

NuGet 是 .NET 官方的包管理器,相当于:

平台包管理器
JavaMaven / Gradle
Node.jsnpm
Pythonpip
RustCargo
GoGo Modules
.NETNuGet

负责:

  • 下载第三方库
  • 管理版本
  • 自动解析依赖
  • Restore(恢复依赖)
  • 更新包

二、NuGet 的发展历程

第一代:packages.config(约 2011~2017)

这是 .NET Framework 时代最经典的管理方式。

目录结构:

Solution
│
├── packages
│     Newtonsoft.Json.13.0.3
│     NLog.5.0.0
│     EmbedIO.3.5.2
│
├── ProjectA
│     packages.config
│
└── ProjectB
      packages.config

每个项目都有:

<?xml version="1.0" encoding="utf-8"?>
<packages>
    <package id="EmbedIO" version="3.5.2" />
    <package id="Scriban" version="7.2.5" />
    <package id="Newtonsoft.Json" version="13.0.3" />
</packages>

优点

  • 简单
  • 容易理解
  • 所有 DLL 都在项目附近

缺点(也是后来被淘汰的原因)

1、每个项目都有 packages

例如:

ProjectA
    packages

ProjectB
    packages

ProjectC
    packages

同一个 Newtonsoft.Json:

ProjectA/packages/Newtonsoft.Json

ProjectB/packages/Newtonsoft.Json

ProjectC/packages/Newtonsoft.Json

重复下载很多次。


2、不能识别传递依赖(Transitive Dependency)

例如:

EmbedIO
    │
    ├── SwanLite
    │      │
    │      └── System.Memory
    │
    └── System.Buffers

packages.config 会写成:

<package id="EmbedIO"/>

<package id="SwanLite"/>

<package id="System.Memory"/>

<package id="System.Buffers"/>

几年以后:

packages.config

100 个 Package

↓

真正需要

30 个

↓

70 个历史遗留

越来越乱。


3、不会自动删除垃圾依赖

例如:

安装:

EmbedIO

后来:

卸载 EmbedIO

NuGet 不知道:

System.Memory

System.Buffers

Unsafe

是不是还能删。

因此不会清理。


4、Restore 不会整理依赖

执行:

nuget restore

只会:

  • 下载缺失包
  • 修复版本

不会:

  • 删除没用的包
  • 重建依赖树

第二代:PackageReference(官方推荐)

2017 年以后微软推出了 PackageReference。

这是目前所有新项目推荐的方式。

它没有:

packages.config

依赖直接写进:

*.csproj

例如:

<ItemGroup>

    <PackageReference Include="EmbedIO"
                      Version="3.5.2"/>

    <PackageReference Include="Newtonsoft.Json"
                      Version="13.0.3"/>

</ItemGroup>

最大区别

PackageReference 只保存:

你主动安装的 Package

例如:

EmbedIO

真正依赖:

EmbedIO
    ↓
SwanLite
    ↓
System.Memory
    ↓
Unsafe

csproj:

<PackageReference Include="EmbedIO"/>

只有这一句。

其它全部由 NuGet 自动计算。


删除包以后

例如:

删除:

<PackageReference Include="EmbedIO"/>

Restore:

dotnet restore

NuGet:

重新计算依赖树

↓

SwanLite

↓

Memory

↓

Unsafe

↓

全部自动消失

这就是 PackageReference 最大优势。


三、PackageReference 的包存放位置

默认:

C:\Users\<用户名>\.nuget\packages

例如:

C:\Users\Admin\.nuget\packages

    embedio

    scriban

    newtonsoft.json

所有项目共享。


为什么这样设计?

以前:

ProjectA

Newtonsoft.Json

ProjectB

Newtonsoft.Json

ProjectC

Newtonsoft.Json

下载三份。

现在:

.nuget

Newtonsoft.Json

所有项目共用。

节省大量磁盘空间。


四、如何修改下载目录(推荐)

很多开发机器 C 盘空间比较紧张,因此建议把全局缓存改到其他盘。

方法一(推荐):NuGet.Config

修改:

%AppData%\NuGet\NuGet.Config

加入:

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <config>

    <add key="globalPackagesFolder"
         value="D:\NuGetPackages" />

  </config>

</configuration>

以后:

D:\NuGetPackages

    embedio

    scriban

    newtonsoft.json

所有项目都会使用该目录。


方法二:环境变量

设置系统环境变量:

NUGET_PACKAGES

例如:

NUGET_PACKAGES=D:\NuGetPackages

优先级高于默认目录。


查看当前缓存位置

dotnet nuget locals global-packages --list

例如:

global-packages:
C:\Users\Admin\.nuget\packages

清空缓存

dotnet nuget locals all --clear

五、是否建议迁移?

新项目

强烈建议使用 PackageReference。

优点:

  • 官方推荐
  • 自动解析依赖
  • 自动管理传递依赖
  • Restore 更快
  • 不会产生大量垃圾依赖
  • 与 .NET Core / .NET 5+ 保持一致

老项目(.NET Framework 4.8)

如果项目:

  • 使用 Visual Studio 2019/2022
  • NuGet 包比较新
  • 没有特别老的第三方包

建议迁移。

Visual Studio:

右键项目

↓

Migrate packages.config to PackageReference...

即可自动完成迁移。


哪些项目暂时不要迁移?

例如:

  • VS2015 以前
  • 非常老的 NuGet 包
  • 特殊 build.targets
  • 自定义 MSBuild 脚本较多

建议先验证兼容性。


六、迁移后的注意事项

迁移成功以后:

应该删除:

packages.config

并且:

csproj 不应该再出现:

<Import Project="packages\..." />

以及:

<Target Name="EnsureNuGetPackageBuildImports">

这些都是 packages.config 时代遗留内容。

如果仍然存在,应删除或重新迁移。


七、Top-level Packages 与 Transitive Packages

迁移时:

Visual Studio 会区分:

Top-level Packages

Transitive Packages

建议:

✅ 保留:

Top-level Packages

不要勾选:

Transitive Packages

原因:

传递依赖由 NuGet 自动管理即可。

只有当你的代码直接引用某个传递包(例如 System.MemoryMicrosoft.Bcl.AsyncInterfaces 等)时,才需要将其作为顶层依赖保留。


八、我的建议(适用于 .NET Framework 4.8)

如果是现在新建或长期维护的项目,我推荐:

  • 优先使用 PackageReference,不要再使用 packages.config
  • 将 NuGet 全局缓存目录修改到 D:\NuGetPackages(或其他非 C 盘目录),避免占用系统盘空间。
  • 保持 .csproj 中只保留真正主动引用的包,让 NuGet 自动管理传递依赖。
  • 定期执行 dotnet nuget locals all --clear 清理缓存(需要时即可,不必频繁)。