【性能优化】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

相关文章

JVisualVM之Java性能监控与调优利器详解

《JVisualVM之Java性能监控与调优利器详解》本文将详细介绍JVisualVM的使用方法,并结合实际案例展示如何利用它进行性能调优,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录1. JVisualVM简介2. JVisualVM的安装与启动2.1 启动JVisualVM2

Java使用MethodHandle来替代反射,提高性能问题

《Java使用MethodHandle来替代反射,提高性能问题》:本文主要介绍Java使用MethodHandle来替代反射,提高性能问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录一、认识MethodHandle1、简介2、使用方式3、与反射的区别二、示例1、基本使用2、(重要)

SpringBoot中HTTP连接池的配置与优化

《SpringBoot中HTTP连接池的配置与优化》这篇文章主要为大家详细介绍了SpringBoot中HTTP连接池的配置与优化的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、HTTP连接池的核心价值二、Spring Boot集成方案方案1:Apache HttpCl

PyTorch高级特性与性能优化方式

《PyTorch高级特性与性能优化方式》:本文主要介绍PyTorch高级特性与性能优化方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、自动化机制1.自动微分机制2.动态计算图二、性能优化1.内存管理2.GPU加速3.多GPU训练三、分布式训练1.分布式数据

MySQL中like模糊查询的优化方案

《MySQL中like模糊查询的优化方案》在MySQL中,like模糊查询是一种常用的查询方式,但在某些情况下可能会导致性能问题,本文将介绍八种优化MySQL中like模糊查询的方法,需要的朋友可以参... 目录1. 避免以通配符开头的查询2. 使用全文索引(Full-text Index)3. 使用前缀索

C#实现高性能Excel百万数据导出优化实战指南

《C#实现高性能Excel百万数据导出优化实战指南》在日常工作中,Excel数据导出是一个常见的需求,然而,当数据量较大时,性能和内存问题往往会成为限制导出效率的瓶颈,下面我们看看C#如何结合EPPl... 目录一、技术方案核心对比二、各方案选型建议三、性能对比数据四、核心代码实现1. MiniExcel

Java的"伪泛型"变"真泛型"后对性能的影响

《Java的伪泛型变真泛型后对性能的影响》泛型擦除本质上就是擦除与泛型相关的一切信息,例如参数化类型、类型变量等,Javac还将在需要时进行类型检查及强制类型转换,甚至在必要时会合成桥方法,这篇文章主... 目录1、真假泛型2、性能影响泛型存在于Java源代码中,在编译为字节码文件之前都会进行泛型擦除(ty

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable