EF Core预编译模型Compiled Model
前言
最近还在和 npgsql 与 EF Core 斗争,由于 EF Core 暂时还不支持 AOT,因此在 AOT 应用程序中使用 EF Core 时,会提示问题:

听这个意思,似乎使用 Compiled Model 可以解决问题,于是就又研究了一下 EF Core 的这个功能。
在 EF Core 中,模型根据实体类和配置构建,默认情况下,每次创建一个新的 DbContext 实例时,EF Core 都会构建模型。对于需要频繁创建 DbContext 实例的应用程序,这可能会导致性能问题。
Entity Framework Core(EF Core)的预编译模型(Compiled Model)对应提供了一种优化,在 EF Core 6 preview 5 中首次增加了这个功能,可以让设计人员预编译模型,避免在后续执行查询时动态生成模型。
预编译模型的优势
- 性能提升:通过预编译模型,可以减少应用程序启动时的开销,特别是对于大型模型。
此处的启动时间,指
DbContext的首次启动时间,由于延迟查询的机制,一般 DbContext 并不会在新建对象时完成启动,而是在首次执行插入或者查询时完成这个过程。
参考下图(来自参考 1):

显然,随着模型的规模增大,启动时间线性增长;但是使用预编译模型后,启动时间和模型大小基本无关,保持在一个极低的水平。
- 一致性:确保每个
DbContext实例使用相同的模型配置。
使用预编译模型
- 生成编译模型:
使用 EF Core 命令行工具,命令:
dotnet ef dbcontext optimize
这将生成 DbContext 的预编译模型。我只有一个 POCO 类,生成了 3 个文件,类名称就是文件名称。
[DbContext(typeof(DataContext))]
public partial class DataContextModel : RuntimeModel
{
static DataContextModel()
{
var model = new DataContextModel();
model.Initialize();
model.Customize();
_instance = model;
}
private static DataContextModel _instance;
public static IModel Instance => _instance;
partial void Initialize();
partial void Customize();
}
public partial class DataContextModel
{
partial void Initialize()
{
var deviceDatum = DeviceDatumEntityType.Create(this);
DeviceDatumEntityType.CreateAnnotations(deviceDatum);
AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
AddAnnotation("ProductVersion", "8.0.0-rc.2.23480.1");
AddAnnotation("Relational:MaxIdentifierLength", 63);
AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel());
}
private IRelationalModel CreateRelationalModel()
{
// 这里面非常多描述类型的代码,节约篇幅我就不写全了。
var relationalModel = new RelationalModel(this);
var deviceDatum = FindEntityType("AspireSample.DeviceDatum")!;
var defaultTableMappings = new List<TableMappingBase<ColumnMappingBase>>();
deviceDatum.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings);
....
return relationalModel.MakeReadOnly();
}
}
internal partial class DeviceDatumEntityType
{
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
{
var runtimeEntityType = model.AddEntityType(
"AspireSample.DeviceDatum",
typeof(DeviceDatum),
baseEntityType);
var id = runtimeEntityType.AddProperty(
"Id",
typeof(string),
propertyInfo: typeof(DeviceDatum).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
fieldInfo: typeof(DeviceDatum).GetField("<Id>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
afterSaveBehavior: PropertySaveBehavior.Throw);
id.TypeMapping = StringTypeMapping.Default.Clone(
comparer: new ValueComparer<string>(
(string v1, string v2) => v1 == v2,
(string v) => v.GetHashCode(),
(string v) => v),
keyComparer: new ValueComparer<string>(
(string v1, string v2) => v1 == v2,
(string v) => v.GetHashCode(),
(string v) => v),
providerValueComparer: new ValueComparer<string>(
(string v1, string v2) => v1 == v2,
(string v) => v.GetHashCode(),
(string v) => v),
mappingInfo: new RelationalTypeMappingInfo(
dbType: System.Data.DbType.String));
......
var key = runtimeEntityType.AddKey(
new[] { id });
runtimeEntityType.SetPrimaryKey(key);
return runtimeEntityType;
}
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
{
runtimeEntityType.AddAnnotation("Relational:FunctionName", null);
runtimeEntityType.AddAnnotation("Relational:Schema", null);
runtimeEntityType.AddAnnotation("Relational:SqlQuery", null);
runtimeEntityType.AddAnnotation("Relational:TableName", "devicedata");
runtimeEntityType.AddAnnotation("Relational:ViewName", null);
runtimeEntityType.AddAnnotation("Relational:ViewSchema", null);
Customize(runtimeEntityType);
}
static partial void Customize(RuntimeEntityType runtimeEntityType);
}
可以看到,优化工具帮我们生成了非常多的代码,尤其是与类型描述相关的代码,因此,如果我们修改模型,那么必须重新执行一遍对应的生成指令。
- 修改 DbContext:
修改你的DbContext类,让它使用这个预编译模型。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
// 指定编译模型的使用
optionsBuilder.UseModel(CompiledModels.MyCompiledModel.Instance);
}
}
权衡利弊
核心优点:
- 提升启动速度,对实体类型较多的
DbContext尤其显著。
缺点:
- 不支持全局查询过滤、
Lazy loading proxies、Change tracking proxies和自定义IModelCacheKeyFactory。 - 每次修改模型都必须重新生成优化代码。
不支持的东西很多,每次修改模型还需要重新生成就非常麻烦,因此,如果不是真的启动速度已经非常慢了不建议使用。
后记
我在使用 EF Core 的 Compiled Model 之后依然提示相同的错误,后来发现错误是从 Reflection 相关类爆出的,而不是 EF Core 的相关类。所以错误里说的 Compiled Model 和 EF Core 的 Compiled Model 概念不同,应该指 AOT 不支持反射中动态加载,需要提前编译。现在 EF Core 还没完全准备好,因此,重申一下,EF Core 8 暂时不支持 AOT。
参考
- Announcing Entity Framework Core 6.0 Preview 5: Compiled Models - .NET Blog (microsoft.com)
- Advanced Performance Topics | Microsoft Learn
EF Core预编译模型Compiled Model的更多相关文章
- EF Core 使用编译查询提高性能
今天,我将向您展示这些EF Core中一个很酷的功能,通过使用显式编译的查询,提高查询性能. 不过在介绍具体内容之前,需要说明一点,EF Core已经对表达式的编译使用了缓存:当您的代码需要重用以前执 ...
- ASP.NET Core MVC 之模型(Model)
1.模型绑定 ASP.NET Core MVC 中的模型绑定将数据从HTTP请求映射到操作方法参数.参数既可以是简单类型,也可以是复杂类型.MVC 通过抽象绑定解决了这个问题. 2.使用模型绑定 当 ...
- [翻译 EF Core in Action 1.9] 掀开EF Core的引擎盖看看EF Core内部是如何工作的
Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...
- [转][ASP.NET]ASP.NET 预编译网站
[转自]https://msdn.microsoft.com/zh-cn/library/ms227430(v=vs.80).aspx C:\Windows\Microsoft.NET\Framewo ...
- 008.Adding a model to an ASP.NET Core MVC app --【在 asp.net core mvc 中添加一个model (模型)】
Adding a model to an ASP.NET Core MVC app在 asp.net core mvc 中添加一个model (模型)2017-3-30 8 分钟阅读时长 本文内容1. ...
- 体验 ASP.NET Core 1.1 中预编译 MVC Razor 视图
这是从 ASP.NET Core 1.1 官方发布博文中学到的一招,可以在 dontet publish 时将 Razor 视图编译为 .dll 文件. 需要在 project.json 中添加如下配 ...
- EF Core中避免贫血模型的三种行之有效的方法(翻译)
Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...
- EF Core使用CodeFirst在MySql中创建新数据库以及已有的Mysql数据库如何使用DB First生成域模型
官方教程:https://docs.microsoft.com/en-us/aspnet/core/data/?view=aspnetcore-2.1 使用EF CodeFirst在MySql中创建新 ...
- 【ASP.NET Core】EF Core 模型与数据库的创建
大家好,欢迎收看由土星卫视直播的大型综艺节目——老周吹逼逼. 今天咱们吹一下 EF Core 有关的话题.先说说模型和数据库是怎么建起来的,说装逼一点,就是我们常说的 “code first”.就是你 ...
- EF Core 配置模型
0 前言 本文的第一节,会概述配置模型的作用(对数据模型的补充描述). 第二节描述两种配置方式,即:数据注释(data annotations)和 Fluent API 方式. 第三节开始,主要是将常 ...
随机推荐
- 本地连接阿里云上的mysql centos
首先写下原因: 未让3306端口通过防火墙 1. 检查端口是否被防火墙挡住 telnet ip地址 3306 在windows中打开telnet应用, 参考:https://www.cnblogs. ...
- pandas 利用openpyxl设置表格样式
writer = pd.ExcelWriter('/home/leo/Desktop/项目测试/922904.xlsx', engine='openpyxl') new_data.to_excel(w ...
- 抽象类 vs 接口【概念解析系列_2】【C# 基础】
〇.前言 抽象类和接口的相似之处还是很多的,但是它们的侧重点不同,本文将简单梳理下. 一.简介与示例 1.1 抽象类 抽象类就是不能使用 new 方法进行实例化的类,即没有具体实例对象的类. 抽象类有 ...
- python教程 入门学习笔记 第4天 数据类型 获取数据类型 字符串拼接
数据类型 1.能直接处理的基本数据类型有5个:整型.浮点型.字符串.布尔值.空 1)整型(int)=整数,例如0至9,-1至-9,100,-8180等,人数.年龄.页码.门牌号等 没有小数位的数字,是 ...
- __wakeup()魔术方法绕过(CVE-2016-7124)
__wakeup()魔术方法绕过(CVE-2016-7124) 漏洞简介 在php反序列化数据过程中,如果类中存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方 ...
- Java日志框架的依赖设置备查(SLF4J, Log4j, Logback)
前言 最近在看代码的过程中,发现身边的许多人在使用Java日志框架时,对于应该引入何种依赖不甚了解,搜索网络上的文章,常常也是互不一致.这篇文章可以看着是Java日志框架的入门使用和实践建议,重点介绍 ...
- Hadoop环境安装与配置
1.基础操作系统环境安装(略) 2.JDK的安装与配置 当前各大数据软件如Hadoop等,仍然停留在Java 8上,在本实验选用的是Java 8.在自己的Linux系统中,jdk可以使用如下命令进行一 ...
- 钉钉旧版服务端SDK支持异步方法的升级改造
最近项目中需要对接钉钉,有些钉钉 API 的访问需要使用旧版服务端 SDK 才能搞定,但是这个 SDK 使用的还是 .NET Framework 2.0 框架,不能跨平台部署,也不支持 async\a ...
- Oracle主键自增列
SQL Server创建主键自增列我们可以使用"ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY"一步到位创建,而Oracle创建主键自增列和SQ ...
- 一文浅谈Mockito使用
一.前期准备- 1.准备工作 <!--mockito依赖-->a <dependency> <groupId>org.mockito</groupId> ...