【性能优化】EFCore性能优化(一)-IEnumerable和IQueryable

阅读本文你的收获

  1. 了解EF Core在使用上应该要注意的地方
  2. 学习EF Core的优化技术,如禁用跟踪、IQueryable接口等

一、问题描述

Entity Framework Core(以下简称EF)是微软自家出品的大型O/RM框架,使用EF Core,我们可以在C#中用操作对象的方式来访问和操作数据库,它可以帮助我们把对C#对象的增删改查,转换成对应的关系型数据库的SQL语句,同时可以把从数据库返回的数据转换成C#的对象,并可以对对象的状态进行跟踪,只要状态发生改变,它就可以帮助我们生成对应的update、insert或者delete的SQL命令。使用EF可以大大提高程序员的开发效率。

但是使用EF的时候,也有不少要注意的地方。比如说EF操作数据库的性能肯定没有直接用ADO.NET操作SQL语句的性能高;另外,如果使用不当,则会导致内存占用过高,系统性能下降的问题等等。

如果你的系统对性能要求很高,我们就必须知道怎么去规避这些问题,在使用EF Core作为ORM时,你必须要注意以下几个方面:

  • IEnumerable和IQueryable两种接口要充分理解,区别使用
  • EF默认自带实体跟踪机制,只读查询用非跟踪式查询可以提高效率。用AsNoTracking方法。
  • EF对批量操作的支持不太高效,所以像批量添加、删除、修改等操作的执行效率,要注意分析与改进。
  • 对于一些复杂查询,EF帮我们生成的SQL语句有时是低效的,这时可以让EF直接执行原生SQL语句,或者改用ADO.NET方式去执行。(关于这一点,请参考 EFCore基础之如何执行原生SQL语句 )

今天先来学习一下IEnumerable和IQueryable接口。

二、理解IEnumerable和IQueryable接口

LINQ标准查询运算符是依靠一组 扩展方法 来实现的。而这些扩展方法分别在System.Linq.Enumerable和System.Linq.Queryable这两个静态类中定义。比如:Where()、Select()、Count()、Any()等方法。

以Where扩展方法为例,在System.Linq.Enumerable类中,是这样定义的,它操作的是IEnumerable< TSource > 类型的集合数据,参数是Func< T, bool >委托类型。

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

在System.Linq.Queryable类中,Where扩展方法是这样定义的,它操作的是IQueryable< TSource >类型的数据,参数一个Expression< T,bool >表达式树类型。

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,Expression<Func<TSource, bool>> predicate);

事实上,IEnumerable是可枚举接口,集合类型如List、Dictionary等都是继承了该接口,它还有一个泛型的版本IEnumerable< T >, 实现了该接口的对象都是集合对象,可以使用foreach接口进行遍历。

对于IEnumerable接口的LINQ扩展方法,可以实现在内存中对这些集合对象进行LINQ查询。使用IEnumerable的例子:

List<Department> data = new List<Department>(); //实例化一个空集合
data.Add(new Department { Id=Guid.NewGuid(), DepName="部门1" });
data.Add(new Department { Id=Guid.NewGuid(), DepName="部门2" });//对集合做一个LINQ查询,是在内存中操作的
IEnumerable<Department>  result = data.Where(x => x.DepName.Contains("1"));//result是一个可枚举对象,所以可以用foreach进行遍历
foreach (var item in result) 
{Console.WriteLine($"{item.Id}数据是:{item.DepName}");
}

IQueryable继承了IEnumerable,IQueryable是可查询接口,采用这个接口去调用LINQ的扩展方法,则可以根据用户指定的条件表达式生成对应的带筛选条件的SQL语句,但是并没有立即执行SQL语句,所以可以继续对这个可查询的对象拼接各种筛选条件、排序、分组等操作,等到执行ToList、Count等方法的时候,Linq to SQL引擎会把表达式树转化成相应的SQL在数据库服务器上执行,再将结果加载到内存中来。这也是Linq的延迟加载核心思想所在,在很复杂的操作下可能比较慢了,时间换空间 以下是使用IQueryable的例子:

/// <summary>
/// 获取车辆分页列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<PageResponseDto<VehicleProductDto>> GetListAsync(QueryVehicleProductDto input)
{//虽然用了where方法,但是并没有立即执行SQL语句IQueryable<VehicleProductInfo> query = from vendor in _vehicleProductRepository.Tablewhere (string.IsNullOrEmpty(input.ItemBrand) || vendor.ItemBrand.Contains(input.ItemBrand))orderby vendor.ItemBrandselect vendor;PageResponseDto<VehicleProductDto> result = new ();//执行分页查询List<VehicleProductInfo> pageList = await query.Skip((input.PageIndex - 1) * input.PageSize) .Take(input.PageSize) //继续拼接 分页的参数,但不执行SQL语句.ToListAsync();       //调用ToList方法则执行SQL语句result.PagedList = pageList.MapToList<VehicleProductDto>(); //通过AutoMapper转换成VehicleProductDtoresult.TotalCount = query.Count();                         //调用Count方法则执行SQL语句return result;
}

三、两者的比较总结

在应用到IEnumberable 和IQueryable两个接口时,代码往往很相似,从而造成了很多困惑,然后从上面分析可以看出,两者还是有很大的区别的,各自都有自己特定的使用场景。

下面是IEnumberable和IQueryable的对比:

项目IEnumerable接口IQueryable接口
命名空间System.CollectionsSystem.Linq
继承于没有基类接口继承于IEnumerable
延迟执行支持支持
延迟加载不支持支持
工作流程把查询的数据加载在内存中,然后再筛选数据,因此这个操作比较占用内存,运行慢当从数据库中查询数据,IQueryable在服务器端根据所有的filter条件执行查询操作,因此该操作需要更少的工作而运行快
适用于LINQ to Object 和LINQ to XML查询LINQ to SQL查询.
使用场合遍历内存中的数据集合(如List,Array等)当查询非内存中的数据集合(如远程数据库,service等)

错误案例演示

//根据输入条件,获取用户分页列表
public PageResponseModel<User> GetUserList(UserPageRequestDto input)
{using MyDbContext db = new MyDbContext();//过早地ToList了,加载全部数据进内存var query = db.Set<User>().AsQueryable().ToList();//拼接查询条件if (string.IsNullOrEmpty(input.Name)){query = query.Where(x => x.UName.StartsWith(input.Name)).ToList();}PageResponseModel<User> pageList = new ();pageList.TotalCount = query.Count();pageList.PageList = query.OrderBy(x=>x.CreateTime).Skip((input.PageIndex-1)*input.PageSize).Take(input.PageSize).ToList();return pageList;
}

以上案例中,因为所有对于IEnumerable的过滤、排序、分组、聚合等操作,都是在内存中进行的,所以在EF Core中使用LINQ查询的时候,如果你不加任何的筛选条件,马上ToList(),然后再进行排序,分组、筛选的话,那也就是说把所有的数据不管用不用得到,都从数据库加载到内存中,只是在内存中进行过滤和排序操作,这样做虽然性能很高,但很消耗内存。属于用“空间换时间”,所以要谨慎使用。

小结

说了这么多,这两者怎么区分使用呢?——操作本地数据源用IEnumerable,操作远程数据源用IQueryable。IQueryable是负责生成SQL语句的,但并不马上执行,可以继续拼接各种的条件到SQL语句中之后再去服务器上执行,占用内存空间小,但是如果查询很复杂,就会比较慢。
IEnumerable是对任意类型的集合都能操作的,不限于是数据库还是一般的数组还是List集合,因为在内存中操作,所以性能高,但是如果你把数据库中的数据全加载进内存来处理,那会很耗内存,导致严重问题。


如果本文对你有帮助的话,请点赞+评论+关注,或者转发给需要的朋友。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/577977.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SD】IP-Adapter 进阶 - 垫图 【画风重绘-必看】

目录 关于SD1.5的画风迁移 修改动作-方法一&#xff1a;提示词 修改动作-方法二&#xff1a;openpose 关于SD1.5的画风迁移 1.5测试模型&#xff1a;flat2DAnimerge_v30_2.safetensors [b2c93e7a89] 测试图&#xff1a; 文生图&#xff1a;best quality,masterpiece, co…

20231225在WIN10下使用SSH连接Ubuntu20.04.6

20231225在WIN10下使用SSH连接Ubuntu20.04.6 2023/12/25 23:03 https://jingyan.baidu.com/article/5552ef479e1856108ffbc9e3.html Win10怎么开启SSH功能 Win10怎么开启SSH功能,下面就一起来看看吧! 工具/原料 华硕天选4 Windows10 方法/步骤 点击左下角的开始菜单,打开Wind…

Redis过期删除策略和内存淘汰策略

1、设置Redis键过期时间 Redis提供了四个命令来设置过期时间&#xff08;生存时间&#xff09;。 EXPIRE <key> <ttl> &#xff1a;表示将键 key 的生存时间设置为 ttl 秒。 PEXPIRE <key> <ttl> &#xff1a;表示将键 key 的生存时间设置为 ttl 毫秒。…

flutter怎么对ReorderableListView中的用于排序的控制手柄进行显示或隐藏

我在使用ReorderableListView创建可排序列表的时候&#xff0c;需要在编辑的时候才显示右侧的控制排序的手柄。研究了半天&#xff0c;配合搜索引擎&#xff0c;才找到正确的方案。 答案很简单&#xff0c;就是在它的属性当中有一个叫做&#xff1a;buildDefaultDragHandles的…

SICP : The Elements of Programming

好的计算机编程语言应具备的三个特性 基础单元表达式&#xff0c;计算机编程语言最最最基础单元&#xff0c;理应具备的表达式组合的能力&#xff0c;能够通过基础单元表达式组合成更复杂的元素抽象的能力&#xff0c;能通过复杂的元素抽象成更高层的单元 基础单元表达式 加 …

计算机硬件 4.2显卡

第二节 显卡 一、基本概念 1.定义&#xff1a;是主机与显示器之间的接口电路&#xff0c;也成为显示适配器&#xff0c;决定显示系统的优劣。 2.作用&#xff1a;处理和存储图形数据&#xff0c;向显示器提供信号、控制显示器显示的重要设备。 3.分类&#xff1a;核芯显卡、…

lvgl 双物理显示器的驱动实现

目录 一、背景 1. 要实现的功能2. lvgl 版本 二、简单粗暴的方式 理论上可以这样实现缺陷&#xff1a; 三、lvgl 自身机制支持 3.1 实现思路3.2 初始化缓冲区和注册显示驱动 3.2.1 复制lv_port_disp → lv_port_disp_23.2.2 修改 lv_port_disp_2 文件3.2.3 在应用层调用显示器…

什么是SLAM中的回环检测,如果没有回环检测会怎样

目录 什么是回环检测 如果没有回环检测 SLAM&#xff08;Simultaneous Localization and Mapping&#xff0c;即同时定位与地图构建&#xff09;是一种使机器人或自动驾驶汽车能够在未知环境中建立地图的同时定位自身位置的技术。回环检测&#xff08;Loop Closure Detectio…

快速入门学习定时任务框架-xxljob

定时任务框架-xxljob 简介 主要用于分布式任务调度&#xff0c;可以将任务调度和执行分布在多个节点上。它提供了一个集中式的管理平台&#xff0c;支持动态添加、修改、删除任务&#xff0c;以及任务的分片执行&#xff0c;确保任务在分布式环境中的高可用性的一个框架 spr…

C语言中函数调用和嵌套

函数是C语言的基本组成元素 函数调用 根据函数在程序中出现的位置有下列三种函数调用方式&#xff1a; 将函数作为表达式调用 将函数作为表达式调用时&#xff0c;函数的返回值参与表达式的运算&#xff0c;此时要求函数必须有返回值 int retmax(100,150); 将函数作为语句…

k8s学习 — (DevOps实践)第十四章 微服务 DevOps 实战 【Kubesphere DevOps】

k8s学习 — &#xff08;DevOps实践&#xff09;第十四章 微服务 DevOps 实战 【Kubesphere DevOps】 1 开启 DevOps2 集成 SonarQube3 更新 settings.xml4 部署项目4.1 Spring Boot 项目4.1.1 项目4.1.2 配置文件 4.2 微服务项目4.2.1 项目4.2.2 配置文件 5 DevOps 项目5.1 Sp…

大数据Doris(四十一):物化视图简单介绍

文章目录 物化视图简单介绍 一、适用场景

Windows系统配置pytorch环境,Jupyter notebook编辑器安装使用(深度学习本地篇)

如今现在好一点的笔记本都自带英伟达独立显卡&#xff0c;对于一些简单的深度学习项目&#xff0c;是不需要连接服务器的&#xff0c;甚至数据量不大的话&#xff0c;cpu也足够进行训练学习。我把电脑上一些以前的笔记整理一下&#xff0c;记录起来&#xff0c;方便自己35岁事业…

在Android中使用Flow获取网络连接信息

在Android中使用Flow获取网络连接信息 如果你是一名Android开发者&#xff0c;你可能会对这个主题感到有趣。考虑到几乎每个应用程序都需要数据交换&#xff0c;例如刷新动态或上传/下载内容。而互联网连接对此至关重要。但是&#xff0c;当用户的设备离线时&#xff0c;数据如…

Python之__enter__和__exit__

1. 概述 这两个函数与with as语句语句紧密相连&#xff0c;即 class Test():def __enter__(self):print("Enter!")passdef __exit__(self, type, value, trace):print("Exit!")passwith Test() as f: # 执行with as语句时&#xff0c;Test类中的_…

git中的smart checkout和force checkout

切换分支时出现了这个问题&#xff1a; 这是因为shiyan01分支修改了代码,但是没有commit, 所以在切换到test分支的时候弹出这个窗口 一、smart checkout(智能签出) 会把shiyan01分支的改动内容带到test分支。合并处理后的内容就变成了test分支的内容,而shiyan01分支的改动会被…

【Qt-编码】

Qt编程指南 ■ 编码■ ASCII■ ANSI■ GB2312■ GBK■ GB18030 编码■ Unicode■ UTF-8&#xff1a; ■ Qt接收注射泵GBK编码后显示乱码■■ ■ 编码 ■ ASCII &#xff08;American Standard Code for Information Interchange&#xff0c;美国信息交换标准代码&#xff09;…

LH7904D 太阳能警示灯 0.4W×2

应用范围: 可安装在电线杆&#xff0c;路灯&#xff0c;围挡&#xff0c;交 通护栏及各种杆式固体等场所起警示作用。 产品特点&#xff1a; 采用进口PS材质; 光控无开关&#xff0c;白天不闪&#xff0c;昏暗环境自动闪烁&#xff0c;无需手动操作&#xff0c;省时省事; …

L4S 杂谈

这不是技术文档&#xff0c;这是技术以外的文档。 带宽资源越来越丰富时&#xff0c;大家反而不抢了&#xff0c;资源越稀缺争抢越厉害&#xff0c;相比丰盈的带宽&#xff0c;如今人们更关注时延。l4s 提供了完全不同的方案。但梳理一下传统方法是必要的。 30 多年来&#x…

SpringBoot Gateway整合过程中的问题

现象描述&#xff1a; 第一次请求正常&#xff0c;第二次报错&#xff0c;错误如下&#xff1a; 2023-12-25 00:47:46.928 ERROR 5484 --- [ctor-http-nio-6] a.w.r.e.AbstractErrorWebExceptionHandler : [34ab38d8-2] 500 Server Error for HTTP GET "/gateway/config…