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

2023-12-26 08:36

本文主要是介绍【性能优化】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集合,因为在内存中操作,所以性能高,但是如果你把数据库中的数据全加载进内存来处理,那会很耗内存,导致严重问题。


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

这篇关于【性能优化】EFCore性能优化(一)-IEnumerable和IQueryable的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/538745

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

一文详解SpringBoot响应压缩功能的配置与优化

《一文详解SpringBoot响应压缩功能的配置与优化》SpringBoot的响应压缩功能基于智能协商机制,需同时满足很多条件,本文主要为大家详细介绍了SpringBoot响应压缩功能的配置与优化,需... 目录一、核心工作机制1.1 自动协商触发条件1.2 压缩处理流程二、配置方案详解2.1 基础YAML

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语

MySQL中慢SQL优化方法的完整指南

《MySQL中慢SQL优化方法的完整指南》当数据库响应时间超过500ms时,系统将面临三大灾难链式反应,所以本文将为大家介绍一下MySQL中慢SQL优化的常用方法,有需要的小伙伴可以了解下... 目录一、慢SQL的致命影响二、精准定位问题SQL1. 启用慢查询日志2. 诊断黄金三件套三、六大核心优化方案方案

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

使用国内镜像源优化pip install下载的方法步骤

《使用国内镜像源优化pipinstall下载的方法步骤》在Python开发中,pip是一个不可或缺的工具,用于安装和管理Python包,然而,由于默认的PyPI服务器位于国外,国内用户在安装依赖时可... 目录引言1. 为什么需要国内镜像源?2. 常用的国内镜像源3. 临时使用国内镜像源4. 永久配置国内镜

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3