目录

枚举<问题>

返回解决方案[0]

但等等,还有更多

或是LinqKit?

总结


从实体框架LINQ查询中提取方法会悄然扼杀性能。这里有三个简单的解决方案,包括:表达式,扩展方法和LinqKit。

枚举<问题>

上周,我惊讶地发现通过提取方法重构实体框架LINQ查询的可读性或可重用性会悄悄地将查询从SQL交换到内存中处理并扼杀性能。

这是我的问题的简化版本。

private async Task<List<User>> GetUsersMatching(IMainFilterDto filter, string prefix)
{var usersQuery = Users.Where(u =>(filter.StartDate == null || u.CreationTime > filter.StartDate) &&(filter.EndDate == null || u.CreationTime <= filter.EndDate) &&u.Name.StartsWith(prefix));return await usersQuery.ToListAsync();
}

我有一个由前端提供的站点范围的过滤对象,但是之后我需要做一些特定于手头任务的其他事情,比如.StartsWith()。

然后在别处,我需要一些非常类似的东西

private async Task<List<User>> GetUsersWithoutRoles(IMainFilterDto filter)
{var usersQuery = Users.Include(i => i.Roles).Where(u =>(filter.StartDate == null || u.CreationTime > filter.StartDate) &&(filter.EndDate == null || u.CreationTime <= filter.EndDate) &&!u.Roles.Any());return usersQuery.ToList();
}

呃。两者之间的共同代码不是DRY,而且感觉很糟糕。如果我需要去改变它,也许通过用>=更换>,我要找出所有与该代码一样的地方。我很想提取它:

private bool ApplyMainFilter(IMainFilterDto filter, User u)
{return (filter.StartDate == null || u.CreationTime > filter.StartDate) &&(filter.EndDate == null || u.CreationTime <= filter.EndDate);
}

并像这样使用它:

private async Task<List<User>> GetUsersWithoutRoles(IMainFilterDto filter)
{var usersQuery = Users.Where(u =>ApplyMainFilter(filter, u) &&u.Name.StartsWith(prefix));

这当然读得更好。当我测试它时,它返回完全相同的结果。遗憾的是,当我通过LINQPad运行它时,原始查询(其中过滤器具有非null开始日期但是null结束日期)由以下:

SELECT [stuff]
FROM [Users] AS [u]
WHERE ([u].[CreationTime] > @__filter_StartDate_0) AND (([u].[Name] LIKE @__prefix_1 + N'%' _
AND (LEFT([u].[Name], LEN(@__prefix_1)) = @__prefix_1)) OR (@__prefix_1 = N''))

变成:

SELECT [stuff]
FROM [Users] AS [u]WHERE ([u].[Name] LIKE @__prefix_1 + N'%' AND _
(LEFT([u].[Name], LEN(@__prefix_1)) = @__prefix_1)) OR (@__prefix_1 = N'')

它删除了ApplyMainFilter()中的所有代码!在这个简单的例子中,这可能看起来并不可怕,但想象一下更复杂的场景。这可能会导致很多更多的记录从数据库中返回。它可能会造成网络瓶颈或对中间件造成过大压力。

最糟糕的是,它可能会阻止数据库执行它最擅长的操作:使用索引来优化查询执行。这可能意味着绕过现有索引,阻止使用未来索引进行查询优化,或者通过完全隐藏数据库中的问题来降低Azure SQL数据库中性能建议的有效性。

顺便提一下,如果你想看一个问题和解决方案的视频,请查看Code Hour的第22集:https://youtu.be/hYry3i5Nvzw

返回解决方案[0]

一旦发现问题,解决方案就变得相当容易了。了解实体框架如何在内部是有帮助的。这是关于表达树的所有内容,我之前已经写过了(好吧,我在11年前写过,但它所描述的基本原理仍然是可靠的)。

预测所有可能的方式,有人可能会将任意C#语言传递给一个where子句并将其全部转换为SQL是一个难题。我需要提供实体框架。一种方法是返回一个完全可解析的表达式树,Expression<func bool=""><Func<User, bool>>而不仅仅是bool或Func<User, bool>。它看起来像这样:

private Expression<Func<User, bool>> GetMainFilterQuery(IMainFilterDto filter)
{return u => (filter.StartDate == null || u.CreationTime > filter.StartDate) &&(filter.EndDate == null || u.CreationTime <= filter.EndDate);
}

执行方式如下:

private async Task<List<User>> GetUsersMatching(IMainFilterDto filter, string prefix)
{var usersQuery = Users.Where(GetMainFilterQuery(filter)).Where(u => u.Name.StartsWith(prefix));

这不是一个美学上令人愉悦的解决方案吗?它可重用,读取良好,并转换为SQL。

但等等,还有更多

但是,如果你想进一步阅读,我想我会提出一个更有趣的选择。如果您使用的是流式API,那么扩展方法方法可能是完美的:

public static class QueryUtils
{public static IQueryable<user> AppendMainFilterQuery(this IQueryable<user> existingQuery, IMainFilterDto filter){return existingQuery.Where(u => (filter.StartDate == null ||  u.CreationTime > filter.StartDate) &&(filter.EndDate == null || u.CreationTime <= filter.EndDate));}
}

这有点难以阅读,但允许这样:

private async Task<List<User>> GetUsersMatching(IMainFilterDto filter, string prefix)
{var usersQuery = Users.Where(u => u.Name.StartsWith(prefix)).AppendMainFilterQuery(filter);

这读得很好,是可重用的,就像第一个解决方案一样,保持SQL的最初状态。

或是LinqKit?

我是由一位聪明的同事来管理这一切的,他建议我查看LinqKit以防万一我需要做更复杂的事情。除此之外,LinqKit允许您跨多个方法构建表达式。例如,如果我需要一个OR子句而不是一个AND子句,它可能看起来像这样:

private ExpressionStarter<User> GetMainFilterPredicate(IMainFilterDto filter)
{var predicate = PredicateBuilder.New<User>().Start(u => (filter.StartDate == null || u.CreationTime > filter.StartDate) &&(filter.EndDate == null || u.CreationTime <= filter.EndDate));return predicate;
}private <list ser="">Task<List<User>> GetUsersMatching(IMainFilterDto filter, string prefix)
{var predicate = GetMainFilterPredicate(filter);predicate = predicate.Or(u => u.Name.StartsWith(prefix));return Users.Where(predicate).ToListAsync();
}

很漂亮。

总结

如果我不需要任何更复杂的东西,我喜欢第一种方法,但无论如何,确定如何不重构LINQ查询是重要的部分。如果您有任何其他创意解决方案,请分享以下评论。

3种重构EF Linq查询的方法而不扼杀性能相关推荐

  1. 几种更新(Update语句)查询的方法

    正 文: 数据库更新就一种方法Update, 其标准格式:Update 表名 set 字段=值 where 条件 不过根据数据的来源不同,还是有所区别的:   1.从外部输入 这种比较简单 例:upd ...

  2. Oracle 几种更新(Update语句)查询的方法

    数据库更新就一种方法Update, 其标准格式:Update 表名 set 字段=值 where 条件 不过根据数据的来源不同,还是有所区别的:   1.从外部输入 这种比较简单 例:update t ...

  3. Asp.Net MVC +EF(2)Linq查询和Lambda表达式

    Linq简介 Linq全称Language Integrated Query(语言集成查询),为我们提供一种统一的方式来查询和操作各种数据. linq to object:是针对实现IEnumerab ...

  4. C#使用linq查询大数据集的方法

    这篇文章主要介绍了C#使用linq查询大数据集的方法,涉及C#调用linq进行数据查询的技巧,具有一定参考借鉴价值,需要的朋友可以参考下 using System; using System.Coll ...

  5. mysql查询耗时_一种数据库高耗时查询的自动取消方法与流程

    本发明涉及数据库的查询方法,特别涉及一种数据库高耗时查询的自动取消方法. 背景技术: 有很多关系型数据库查询业务非常耗时,比如查询企业实时报表之类的,一次查询可能需要几分钟甚至更长.在很多时候,前端业 ...

  6. LINQ to CSV,一种类型安全,动态的高性能方法

    目录 介绍 背景 使用代码 输入CSV文件 设计动机 类型安全--运行时类型安全检查 改变自我,动态类型 优缺点与性能 文件夹结构 简短的代码演练 限制和假设 结论 下载完整的源代码 介绍 语言集成查 ...

  7. 同步压缩变换 matlab,一种基于同步压缩变换重构的幅值校正方法

    一种基于同步压缩变换重构的幅值校正方法 [技术领域] [0001] 本发明属于信号处理技术领域,具体设及一种基于同步压缩变换重构的幅值校正 方法. [背景技术] [0002] 时频分析方法是处理非平稳 ...

  8. 几种在NCBI中查询获取目的基因序列的方法

    几种在NCBI中查询获取目的基因序列的方法 在NCBI中,如何查询并下载获得某物种的某特定功能的基因序列,相信对于看到此篇的大部分同学来说都不陌生了.想到对于刚开始接触生信的同学们来说,也许尚不能很熟 ...

  9. asp.net linq查询环境搭建

    本文是以sqlserver2008为数据库,vs2013为开发工具来介绍的. 要搭建这样一个数据库的操作环境,首先建立一个类库项目 然后在这个类库项目中添加几个类:DBDataContext数据库上下 ...

最新文章

  1. reloaddata 跳动_纸跳动像素
  2. Windows下 Python3.7.0 运行环境的搭建 一套操作后就可以使用Python写代码啦~
  3. Android开发学习笔记-自定义组合控件
  4. 案例:Redis 问题汇总和相关解决方案
  5. 面向多星多任务的大数据处理系统设计
  6. [转]聚集索引和非聚集索引(sql server索引结构及其使用)
  7. 特斯拉涨价:Model 3价格上调1万元 Model Y上调2.1万
  8. ppsspp模拟java按键_ppsspp 手柄按键映射 北通 BTP
  9. 理论到实践带你了解情感分析、信息抽取、搜索推荐等NLP相关任务
  10. NLP学习—7.CNN与TextCNN
  11. 2440 OV9650 C通道保存图片完全成功!顶!
  12. 2022-02-27周报
  13. 算法:凸多边形最优三角剖分
  14. mysql计算员工年终奖金_年终奖 扣税计算
  15. 聚合购物一站式采购平台HTML网站源码
  16. 从北京回来的年轻人,我该告诉你点什么? 1
  17. Cocoa-Cocoa框架
  18. SAR+L波段+森林生物量估计+高度反演
  19. 基于Transformer的文本情感分析编程实践(Encoder编码器-Decoder解码器框架 + Attention注意力机制 + Positional Encoding位置编码)
  20. 杰理之AUX/LINEIN 设计注意【篇】

热门文章

  1. python list 元素位置_Python将list中某个元素移至末尾
  2. ul c语言,IMX6UL裸机实现C语言蜂鸣器实验
  3. mysql 电商实战_电商项目测试实战(一)
  4. 什么是徽标LOGO模板?
  5. 手机支付优惠促销活动插画素材,拿来就能用,高效省时。
  6. C++设计模式详解之外观模式解析
  7. MyBatis的优化
  8. CAS:compare and swap
  9. C语言extern用法
  10. 的电路接法_基本震荡电路知多少,汇总几种震荡电路的接法!