前言

最近还在和 npgsqlEF 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 中首次增加了这个功能,可以让设计人员预编译模型,避免在后续执行查询时动态生成模型。

预编译模型的优势

  1. 性能提升:通过预编译模型,可以减少应用程序启动时的开销,特别是对于大型模型。

此处的启动时间,指 DbContext 的首次启动时间,由于延迟查询的机制,一般 DbContext 并不会在新建对象时完成启动,而是在首次执行插入或者查询时完成这个过程。

参考下图(来自参考 1):

显然,随着模型的规模增大,启动时间线性增长;但是使用预编译模型后,启动时间和模型大小基本无关,保持在一个极低的水平。

  1. 一致性:确保每个 DbContext 实例使用相同的模型配置。

使用预编译模型

  1. 生成编译模型

    使用 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);
}

可以看到,优化工具帮我们生成了非常多的代码,尤其是与类型描述相关的代码,因此,如果我们修改模型,那么必须重新执行一遍对应的生成指令。

  1. 修改 DbContext

    修改你的 DbContext 类,让它使用这个预编译模型。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
// 指定编译模型的使用
optionsBuilder.UseModel(CompiledModels.MyCompiledModel.Instance);
}
}

权衡利弊

核心优点:

  1. 提升启动速度,对实体类型较多的 DbContext 尤其显著。

缺点:

  1. 不支持全局查询过滤、Lazy loading proxiesChange tracking proxies 和自定义 IModelCacheKeyFactory
  2. 每次修改模型都必须重新生成优化代码。

不支持的东西很多,每次修改模型还需要重新生成就非常麻烦,因此,如果不是真的启动速度已经非常慢了不建议使用

后记

我在使用 EF Core 的 Compiled Model 之后依然提示相同的错误,后来发现错误是从 Reflection 相关类爆出的,而不是 EF Core 的相关类。所以错误里说的 Compiled Model 和 EF Core 的 Compiled Model 概念不同,应该指 AOT 不支持反射中动态加载,需要提前编译。现在 EF Core 还没完全准备好,因此,重申一下,EF Core 8 暂时不支持 AOT

参考

EF Core预编译模型Compiled Model的更多相关文章

  1. EF Core 使用编译查询提高性能

    今天,我将向您展示这些EF Core中一个很酷的功能,通过使用显式编译的查询,提高查询性能. 不过在介绍具体内容之前,需要说明一点,EF Core已经对表达式的编译使用了缓存:当您的代码需要重用以前执 ...

  2. ASP.NET Core MVC 之模型(Model)

    1.模型绑定 ASP.NET Core MVC 中的模型绑定将数据从HTTP请求映射到操作方法参数.参数既可以是简单类型,也可以是复杂类型.MVC 通过抽象绑定解决了这个问题. 2.使用模型绑定 当 ...

  3. [翻译 EF Core in Action 1.9] 掀开EF Core的引擎盖看看EF Core内部是如何工作的

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  4. [转][ASP.NET]ASP.NET 预编译网站

    [转自]https://msdn.microsoft.com/zh-cn/library/ms227430(v=vs.80).aspx C:\Windows\Microsoft.NET\Framewo ...

  5. 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. ...

  6. 体验 ASP.NET Core 1.1 中预编译 MVC Razor 视图

    这是从 ASP.NET Core 1.1 官方发布博文中学到的一招,可以在 dontet publish 时将 Razor 视图编译为 .dll 文件. 需要在 project.json 中添加如下配 ...

  7. EF Core中避免贫血模型的三种行之有效的方法(翻译)

    Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...

  8. EF Core使用CodeFirst在MySql中创建新数据库以及已有的Mysql数据库如何使用DB First生成域模型

    官方教程:https://docs.microsoft.com/en-us/aspnet/core/data/?view=aspnetcore-2.1 使用EF CodeFirst在MySql中创建新 ...

  9. 【ASP.NET Core】EF Core 模型与数据库的创建

    大家好,欢迎收看由土星卫视直播的大型综艺节目——老周吹逼逼. 今天咱们吹一下 EF Core 有关的话题.先说说模型和数据库是怎么建起来的,说装逼一点,就是我们常说的 “code first”.就是你 ...

  10. EF Core 配置模型

    0 前言 本文的第一节,会概述配置模型的作用(对数据模型的补充描述). 第二节描述两种配置方式,即:数据注释(data annotations)和 Fluent API 方式. 第三节开始,主要是将常 ...

随机推荐

  1. django.db.utils.OperationalError: (1366, "Incorrect string value: '\\xE5\\xA4\\xAB\\xE4\\xBA\\xBA' f

    1.打开mysql命令行 show variables like '%char%'; 将字符集显示不是utf-8的更改为utf-8 例如:set character_set_database=utf8 ...

  2. jmeter:json提取一个字段的多个值,用逗号分隔

    目的:将接口响应结果中的一个字段的所有值同时提取,作为参数传给下个接口 1. 格式化接口响应结果 获取下图中所有object里面的EMP_UID 2. json提取 JSON Path express ...

  3. 将Python打包成exe

    使用以下命令首先安装包 pip install pyinstaller 参数以及用法 -F生成结果是一个exe文件,所有的第三方依赖.资源和代码均被打包进该exe内 -D生成结果是一个目录,各种第三方 ...

  4. IDA的使用2

    IDA的使用2 string类型的选择 Rename 要注意如果再namelist和public name里面是不能重名 操作数 这个主要和开发结合精密, change sign-改变符号 bitwi ...

  5. dBeaver操作iotdb并实现导入和导出

    1.windows下操作iotdb,现在官网下载相关的iotdb包 官网地址:https://archive.apache.org/dist/iotdb/   一般建议下载 -all的  2.打开db ...

  6. 3.你所不知道的go语言控制语句——Leetcode习题69

    目录 本篇前瞻 Leetcode习题9 题目描述 代码编写 控制结构 顺序结构(Sequence) 声明和赋值 多返回值赋值 运算符 算术运算符 位运算符 逻辑运算 分支结构 if 语句 switch ...

  7. 设置服务账号Service Accounts(sa)的token不挂载到pod

    目录 一.系统环境 二.前言 三.Service Accounts(sa)简介 四.在pod里设置sa的token不挂载到pod 五.在sa里设置sa对应的token不挂载到pod上 六.总结 一.系 ...

  8. Anaconda平台下从0到1安装TensorFlow环境详细教程(Windows10+Python)

    1.安装Anaconda Anaconda下载链接:Free Download | Anaconda 下载完成之后,开始安装,修改安装路径至指定文件夹下,由于安装过程比较简单,此处略过: 2.Tens ...

  9. 有Root与无Root安装git-lfs

    有Root与无Root安装git-lfs 直接安装 先查看arm还是AMD 例如当前使用Rocky Linux 8.8版本的内核.因此,应该下载适用于Rocky Linux 8.x的Git LFS安装 ...

  10. word2010中统一调整表格格式

    word中统一调整表格格式基本思路是: 1.选中所有的表格. 2.再对表格格式调整.    选中所有表格需要用到宏,操作很简单,具体操作如下: (1)工具栏"视图"下右下角&quo ...