compiled model 是 ef core 7.0+ 提供的预编译模型机制,用于加速 dbcontext 初始化,适用于启动敏感、模型庞大、冷启动频繁或 dbcontext 高频创建的场景,不适用于动态模型、低版本 ef core 或运行时修改元数据。

Compiled Model 是什么,什么时候值得用
EF Core 的 CompiledModel 是将模型构建过程(解析实体、关系、索引、约束等)提前编译为可复用的二进制结构,在应用启动时跳过运行时反射和元数据推导。它不改变查询行为,只加速 DbContext 初始化阶段。
适用于:
- 启动耗时敏感的服务(如 Azure Functions、Kestrel 微服务)
- 模型庞大(>50 个实体,含复杂继承或 TPH 配置)
- 容器环境冷启动频繁(如 Serverless)
-
DbContext实例化频率高(非单例 + Scoped 混用场景)
不适用于:
- 模型动态生成(如运行时拼接
EntityTypeBuilder) - 使用了
IMutableModel或手动修改模型元数据 - EF Core 版本低于 7.0(
CompiledModel从 7.0 正式引入,6.x 仅实验性支持)
如何生成并加载 Compiled Model
EF Core 不提供自动编译命令,需在构建时调用 DbContextOptionsBuilder.UseCompiledModel 并传入预编译对象。
生成步骤:
- 在项目中添加
Microsoft.EntityFrameworkCore.Design包(v7.0+) - 编写一个独立控制台程序(或 MSBuild Target),构造你的
DbContext并调用context.GetService<imodel>().GetModel()</imodel>获取IModel - 调用
CompiledModel.Create(model)(需引用Microsoft.EntityFrameworkCore.Relational) - 序列化结果(推荐
BinaryFormatter已弃用,改用System.Text.Json+ 自定义序列化器,或直接保存为.dat文件)
加载方式(Startup 中):
var compiledModel = LoadCompiledModelFromDisk(); // 自行实现反序列化
optionsBuilder.UseSqlServer(connectionString)
.UseCompiledModel(compiledModel);注意:编译后的模型与 EF Core 运行时版本、Provider 类型(如 SqlServer vs Sqlite)、甚至数据库兼容级别强绑定。换版本或 Provider 必须重新生成。
常见失败现象和绕不过去的坑
- 启动报错
InvalidOperationException: The compiled model is not compatible with the current DbContext type:说明 DbContext 类签名(如泛型参数、基类、构造函数)在编译后发生了变更,哪怕只是加了个私有字段。必须重建 CompiledModel。
- 查询时报
NullReferenceException 在 RelationalModelSnapshot 相关路径:通常是 Provider 版本不一致(如编译用 EF Core 7.0.10,运行时加载 7.0.5),建议锁定 minor 版本。
- 性能没提升甚至变慢:多见于小模型(EnableDetailedErrors /
EnableSensitiveDataLogging,这些开关会绕过部分编译优化。
- 单元测试失败:因测试常使用内存数据库(
InMemory),而 CompiledModel 绑定的是具体 Provider(如 SqlServer),无法混用。测试环境应跳过 UseCompiledModel。
替代方案对比:Source Generators 和常规优化
InvalidOperationException: The compiled model is not compatible with the current DbContext type:说明 DbContext 类签名(如泛型参数、基类、构造函数)在编译后发生了变更,哪怕只是加了个私有字段。必须重建 CompiledModel。 NullReferenceException 在 RelationalModelSnapshot 相关路径:通常是 Provider 版本不一致(如编译用 EF Core 7.0.10,运行时加载 7.0.5),建议锁定 minor 版本。 EnableSensitiveDataLogging,这些开关会绕过部分编译优化。 InMemory),而 CompiledModel 绑定的是具体 Provider(如 SqlServer),无法混用。测试环境应跳过 UseCompiledModel。EF Core 8 引入了 DbContext 源生成器([DbContextSourceGeneration]),它在编译期生成模型代码而非二进制模型,更安全、可调试、不依赖运行时版本对齐。但目前仅支持 Code-First 且不支持所有 Fluent API(如 HasIndex().IsUnique() 的某些链式调用可能被忽略)。
相比而言,CompiledModel 更“暴力”也更底层,适合已有大型遗留模型、不想重构配置逻辑的团队。但它要求你承担版本管理、CI/CD 中模型缓存、以及 Provider 锁定的成本。
真正影响启动性能的,往往不是模型本身,而是连接字符串解析、证书加载、DNS 查询、或第三方 IDbContextFactory 初始化逻辑。别一上来就编译模型——先用 dotnet-trace 录一段启动过程,确认瓶颈真在 ModelBuilding 阶段。











