排除 EF Core Migrations 自动生成文件提升编译优化速度
一、问题背景
当前一个基于 ABP 框架的大规模 .NET 项目(4-5M行代码),目标框架是 net5.0。解决方案里包含多个项目,比如:
XXX.XXX.Web:Web 宿主项目,有Program.cs,负责启动应用。XXX.XXX.StockManage.HttpApi:HTTP API 模块,是一个类库,没有Program.cs。XXX.EFCoreMigration.Shared:EF Core 迁移共享项目,里面存放数据库迁移文件。
项目经过多次迭代,EF Core 的 Migrations 目录下积累了很多迁移文件。每个迁移通常包含:
xxxx_MigrationName.cs:迁移逻辑。xxxx_MigrationName.Designer.cs:迁移元数据。XxxDbContextModelSnapshot.cs:当前数据库模型的快照。
日常开发使用 Debug 配置编译。编译速度越来越慢,改一点代码重新编译也要等很久。后来修改了两个 .csproj 文件,编译速度明显变快。这篇文章记录一下原因和做法。
二、原因
编译变慢主要有两个方面的原因。
1. EF Core 的 .Designer.cs 和 ModelSnapshot.cs 代码量很大
EF Core 每次执行 Add-Migration 都会生成 .Designer.cs 文件。ModelSnapshot.cs 也会在迁移过程中更新。这些文件会把整个 DbContext 的模型信息展开成 C# 代码。
例如,里面会有每个实体、每个属性、每个索引、每个外键的配置。一个中等规模的项目,单个 ModelSnapshot.cs 可能几万行。如果迁移文件很多,所有 .Designer.cs 加在一起可能几十万行。
这些文件默认会参与编译。C# 编译器需要对这些代码做词法分析、语法分析、语义绑定、生成 IL。代码量越大,编译时间越长。每次 Debug 构建都会重新处理这些文件,增量编译也会受影响。
2. .NET SDK 和一些 NuGet 包会带来额外构建开销
项目目标框架是 net5.0,这个版本已经停止支持。新版 .NET SDK 在构建时会检查目标框架是否 EOL,还会检查 workload 是否 EOL。这些检查每次构建都会执行,带来固定开销。
另外,NuGet 包除了程序集,还可能带有 build 和 buildTransitive 目录下的 .props 和 .targets 文件。MSBuild 会自动导入这些文件,可能注册额外的 Target、代码生成任务或分析器。这些也会增加构建时间。
三、解决逻辑
解决思路是:日常开发编译不需要迁移的 .Designer.cs 文件。这些文件只在执行 dotnet ef migrations add 或 dotnet ef database update 时才需要。所以可以在 Debug 配置下把它们从编译项中排除。
同时,关闭 SDK 的 EOL 检查、workload 检查,减少每次构建的固定开销。对于确认不需要 build 逻辑的包,排除 build 和 buildTransitive 资源,减少 MSBuild 节点。
这样做的目的是:让 Debug 构建只编译真正需要修改和运行的代码,把不参与日常开发的自动生成文件排除掉。
四、解决方案
修改了两个 .csproj 文件。
1. XXX.EFCoreMigration.Shared.csproj
在 PropertyGroup 中增加:
<DefaultItemExcludes Condition="'$(Configuration)' == 'Debug' ">$(DefaultItemExcludes);Migrations\**\*.Designer.cs</DefaultItemExcludes>
<DefaultItemExcludes Condition="'$(Configuration)' == 'Debug' ">$(DefaultItemExcludes);Migrations\**\**\*.Designer.cs</DefaultItemExcludes>
这两行的作用是:在 Debug 配置下,把 Migrations 目录下所有 .Designer.cs 文件排除出编译。第一条覆盖一级子目录,第二条覆盖多级子目录。Release 配置下不排除,避免影响迁移生成。
2. XXX.XXX.Web.csproj
在 PropertyGroup 中增加:
<DisableGclm>true</DisableGclm>
<CheckEolWorkloads>false</CheckEolWorkloads>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<DisableEolTargetFrameworkWarning>true</DisableEolTargetFrameworkWarning>
<ExcludeAssets>build,buildTransitive</ExcludeAssets>
这些设置的作用:
CheckEolTargetFramework、CheckEolWorkloads、DisableEolTargetFrameworkWarning:关闭目标框架 EOL 检查,减少警告和检查任务。DisableGclm:关闭 MSBuild 的一个内部开关,减少编译器内部开销。SuppressTfmSupportBuildWarnings:减少构建日志输出。ExcludeAssets=build,buildTransitive:排除 NuGet 包的 build 资源,减少 MSBuild 导入和执行的任务。
注意:ExcludeAssets 要谨慎使用。如果某个包依赖自己的 build targets 才能工作,排除后可能会出问题。
五、可能仍然存在的困境
这些修改能加快编译,但也有一些需要注意的地方。
-
排除
.Designer.cs后,不能在排除的项目里直接执行dotnet ef migrations add。生成迁移时 EF Tools 需要这些文件参与编译。通常做法是到专门的迁移项目执行,或者切换到 Release 配置。 -
ExcludeAssets=build,buildTransitive是项目级设置,会影响所有引用的包。如果发现某个包功能异常,比如源生成器不工作、某个 Target 没执行,要排查是不是被这条设置干掉了。 -
这些优化主要针对 Debug 构建。Release 或 CI 发布时是否保留,取决于团队是否需要发布包执行迁移。我们目前在 Debug 下排除,Release 保留。
-
编译速度慢可能还有其他原因,比如大量分析器、源生成器、项目引用太多、增量编译失效等。这次优化只是减少了一部分开销,不是解决所有问题。
-
如果以后升级到 net6.0 或更高版本,EOL 检查相关的设置可能不再必要,但排除
.Designer.cs的做法仍然有效。 -
需要团队统一约定,避免有人在 Debug 下执行迁移命令导致报错。
总结
这次编译速度优化中,收益最大的是排除 EF Core 迁移的 .Designer.cs 文件。这些文件代码量很大,日常开发又不需要参与编译。关闭 SDK 的额外检查和 NuGet 包的 build 资源也有帮助,但量级小一些。整体思路是:找出不必要参与构建的内容,把它们从 Debug 构建中移除。

浙公网安备 33010602011771号