linq查询不包含某个值的记录_【翻译】C#表达式中的动态查询

当您使用LINQ来处理数据库时,这种体验是一种神奇的体验,对吗?你把数据库实体像一个普通的收集,使用Linq中像WhereSelect或者 Take,这些简单的使用就能让代码可用了。

但是,让我们考虑一下这里是如何通过动态查询和表达式树实现此功能的:幕后发生的事情。您编写的LINQ查询将转换为SQL(或其他方式),并将该SQL查询发送到数据库。然后将数据库的响应映射到C#对象。但是,如何完全转换为SQL?

在本文中,您将看到诸如Entity Framework和MongoDB C#驱动程序之类的框架如何使用表达式树进行转换。您将看到如何亲自使用表达式树来构建动态查询。这些查询是您无法在编译时创建的查询,因为您将知道该查询仅在运行时的外观。

对可查询树和表达式树进行揭秘

考虑以下使用Entity Framework 6的C#代码:

DbSet<Student> students = context.Students;var billie = await students.Where(s => s.StudentName == "Billie").ToListAsync();

执行时,实体框架会产生以下SQL查询:

SQL:SELECT    [Extent1].[StudentID] AS [StudentID],    [Extent1].[StudentName] AS [StudentName],    [Extent1].[DateOfBirth] AS [DateOfBirth],    FROM [dbo].[Students] AS [Extent1]    WHERE N'Billie' = [Extent1].[StudentName]

请注意,WHERESQL查询中有一个操作。那不是很明显。如果SQL不包含WHERE,则所有学生都将从数据库中带走,并且筛选将在.NET进程中执行。实际上,以下代码可以做到这一点:

//BAD:DbSet<Student> students = context.Students;Func<Student, bool> predicate = s => s.StudentName == "Billie";var x = students.Where(predicate).ToList();

在最后一个示例中,SQL查询使所有学生进入流程,并将其映射到常规集合。不同之处在于,在第一段代码中,lambda是一个Expression>,它允许实体框架将其添加到SQL查询中。在第二段代码中,lambda是a Func,因此将Where执行操作符之前的所有操作并将其转换为常规IEnumerable集合,然后执行其余的查询。

第二段代码在性能,内存和网络方面很糟糕。我们从网络中获取了许多对象,而不是仅从数据库中获取一个项目。然后,我们使用CPU将它们序列化为C#对象。并用完内存将它们存储在进程的堆中。

因此,让我们回到第一段代码。如何await students.Where(s => s.StudentName == "Billie").ToListAsync()产生一个包含的SQL查询WHERE N'Billie' = [Extent1].[StudentName]

答案是表达树。该代码s => s.StudentName == "Billie"实际上是一个结构化查询,可以通过编程将其分解为节点树。在此示例中,有6个节点。最顶层的节点是lambda表达式。左侧是lambda参数。在它的右边是Equal表示表达式的lambda主体。实体框架具有遍历这些表达式树并构造SQL查询的算法。其他数据源提供程序(如Mongo DB C#驱动程序)也会发生同样的事情,除了它会构造一个MongoDB json查询。

a3f9fb04a44f24b2c861cc3bd7bc9ccc.png

C#表达式树

在第一段代码中,类型s => s.StudentName == "Billie"Expression>。这表示生成树的表达式树Func。该Where子句接受这种类型的参数,因为aDbSet实现了IQueryable接口,这要求它与表达式树配合使用。相反,常规集合(如数组或a List)IEnumerable意味着它将lambdas => s.StudentName == "Billie"用作常规函数。

好的,但是我该如何利用它呢?

在大多数情况下,使用表达树的人们就是在构建世界实体框架的人们。但是在某些特定情况下,它变得非常有用。这是我们最近在Ozcode[1](我的日常工作)中遇到的一个用例:

我们想在名为Error的数据库实体上创建动态服务器端过滤。该实体具有许多属性,我们希望允许用户对其进行过滤。因此过滤应根据被允许UsernameCountryVersion,或任何其他财产。这是我们需要实现的API:

IQueryable<Error> _errors; public IEnumerable<Error> GetErrors(string propertyToFilter, string value){ /*..*/} 

在这种情况下,propertyToFilter是的属性Error。使用常规的LINQ,唯一的方法就是使用巨大的switch / case语句。有点像这样:

IQueryable<Error> _errors;public IEnumerable<Error> GetErrors(string propertyToFilter, string value){    switch (propertyToFilter)    {        case "Username":            return await _errors.Where(e=> e.Username == value).ToListAsync();        case "Country":            return await _errors.Where(e=> e.Country == value).ToListAsync();        case "Version":            return await _errors.Where(e=> e.Version == value).ToListAsync();        // ...            }}

您可能会同意这不是理想的选择。除了必须编写所有这些东西之外,它还非常容易出现错误。如果添加了属性怎么办?如果重命名怎么办?整个事情一团糟。

通过动态查询和表达式树可以实现此功能的方法如下:

private async static Task<IEnumerable<Error>> GetErrors(string propertyToFilter, string value){    var error = Expression.Parameter(typeof(Error));    var memberAccess = Expression.PropertyOrField(error, propertyToFilter);    var exprRight = Expression.Constant(value);    var equalExpr = Expression.Equal(memberAccess, exprRight);    Expression<Func<Error, bool>> lambda = Expression.Lambda<Func<Error, bool>>(equalExpr, error);    return await _errors.Where(lambda).ToListAsync();}

这里的每一行代码代表表达式树中的一个节点。它们共同构成了最高节点-lambda。然后,可以在LINQ中使用动态表达式,并生成服务器端SQL查询。我认为很好。

解决此问题的另一种方法是构建自定义SQL查询字符串。在Ozcode中,我们使用的是MongoDB,因此SQL不适合使用,但我们可以创建一个自定义的MongoDB JSON查询字符串。也不是太难,但是我认为表达式树方法更加灵活和可靠。一方面,您可以将其放在LINQ中并与其他LINQ运算符组合。此外,当有诸如Entity Framework之类的经过测试的框架可以为您执行此操作时,为什么还要编写自己的查询。

概要

回顾一下。这是本文中的一些关键点:

•常规函数/委托与表达式之间的区别在于,表达式可以用结构化树表示。可以轻松地分析该树以创建诸如数据库查询之类的东西。•支持表达式的数据源实现该IQueryable接口。•如果您无法使用表达式(以及使用常规方法或委托),则查询将在服务器端而不在数据库端,这对于性能而言将是可怕的。•使用lambda(不带主体)时,表达式是无缝创建的,因此这些年来您可能一直都在这样做。•您可以自己使用表达式树来创建动态查询。这在无法在编译时仅在运行时构建查询的情况下很有用。

References

[1] Ozcode: https://oz-code.com

[2]: https://www.mediavine.com/

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

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

相关文章

如何用Python画一个中国地图?

为什么是Python&#xff1f;先来聊聊为什么做数据分析一定要用Python或R语言。编程语言这么多种&#xff0c;Java, PHP都很成熟&#xff0c;但是为什么在最近热火的数据分析领域&#xff0c;很多人选择用Python语言&#xff1f;数据分析只是一个需求&#xff0c;理论上来讲&…

计算机二级ppt为客户制作演示文稿,计算机二级ppt真题:制作介绍北京ppt

1.演示文稿题1具体如图示。2.【打开】文件夹&#xff0c;选择【PPT素材.docx】&#xff0c;点击【选择】选定所有格式类似的文本(无数据)&#xff0c;点击【段落】&#xff0c;调整【大纲级别】1级。选择【蓝色文本】&#xff0c;点击【选择】选定所有格式相似的文本(无数据)&a…

牛气!.NET5电商平台轻松承接10亿GMV,只因做对了这个!

自从京东和唯品会转了Java..NET就一直缺乏高并发电商案例&#xff0c;.NET5能做高并发电商吗&#xff1f;必须的&#xff01;这里为大家分享一家上市电商公司项目案例&#xff1a;纯.NET5电商平台&#xff0c;轻松承接双11当天近10亿GMV&#xff01;系统架构为.NET5微服务Docke…

[笔记].STM32 BOOT[0:1]启动设置

BOOT[0:1] 0? 用户闪存存储器 10 系统存储器 11 内嵌SRAM转载于:https://www.cnblogs.com/yuphone/archive/2011/04/16/2017672.html

项目进度计划甘特图_项目管理进度计划表制作及甘特图绘制方法

全文总计1995字&#xff0c;需阅读5分钟&#xff0c;以下为正文&#xff1a;甘特图(Gantt Chart)又称横道图&#xff0c;它通过图示形象地表示特定项目的活动顺序与持续时间。Excel 2007/2010中&#xff0c; 通过堆积条形图实现。下面以Excel 2010为例来说明绘制步骤上图A列为某…

华科计算机课程设计,华中科大操作系统课程设计报告(附源码).doc

华中科技大学计算机学院操作系统课程设计报告班级&#xff1a;学号&#xff1a;姓名&#xff1a;彭博时间&#xff1a;2010年3月设计内容一&#xff1a;熟悉和理解Linux编程环境编写一个C程序&#xff0c;实现文件拷贝功能。2)编写一个C程序&#xff0c;使用下的图形库&#xf…

这份深度学习课程笔记获吴恩达点赞

吴恩达在推特上展示了一份由 TessFerrandez 完成的深度学习专项课程信息图&#xff0c;这套信息图优美地记录了深度学习课程的知识与亮点。因此它不仅仅适合初学者了解深度学习&#xff0c;还适合机器学习从业者和研究者复习基本概念。机器之心认为这不仅仅是一份课程笔记&…

Android添加单元测试的方法与步骤

一、修改配置文件AndroidManifest.xml <?xml version"1.0" encoding"utf-8"?><manifest xmlns:android"http://schemas.android.com/apk/res/android"package"cn.ycmoon.test.activity"android:versionCode"1"an…

带栩字的优美古诗句_带栩字有寓意的男孩名字

栩字的五行属木&#xff0c;用作人名时意指生动、传神之义&#xff1b;有栩栩如生、巧夺天工等好寓意&#xff0c;用于男孩取名时也是非常的好听有内涵&#xff0c;一起往下看看吧&#xff01;【一】栩字取名男孩名字古风的栩翰翰字是指长而坚硬的羽毛&#xff0c;亦指毛笔、文…

这套前端可视化框架,让数据栩栩如生!

AntV&#xff0c;蚂蚁出品&#xff0c;前端数据可视化&#xff0c;有这一套就够了&#xff01;随着大数据的发展&#xff0c;人们越来越多地使用数据分析来解决问题。为了提高数据分析的效率&#xff0c;各种先进的数据可视化工具应需而生&#xff0c;可以直接根据指定的数据源…

AlphaGo背后的力量:蒙特卡洛树搜索入门指南

我们都知道 DeepMind 的围棋程序 AlphaGo&#xff0c;以及它超越人类的强大能力&#xff0c;也经常会听到「蒙特卡洛树搜索」这个概念。事实上&#xff0c;蒙特卡洛树搜索是在完美信息博弈场景中进行决策的一种通用技术&#xff0c;除游戏之外&#xff0c;它还在很多现实世界的…

js vue中得延时器_Vue.js实现时分秒倒计时

我们平常浏览网页的时候&#xff0c;经常见到“距游戏公测1天2小时3分钟4秒”这样的倒计时器。时间如沙漏般一点点的减少&#xff0c;不仅能挑起用户的兴趣&#xff0c;而且让页面提升了一点逼格&#xff0c;还填补掉一些尴尬的空白位置。最近写得越来越多&#xff0c;有用没用…

文件的搜索

which 显示一个可执行文件的完整路径&#xff1b; [rootberyl ~]# which ls alias lsls --colortty /bin/ls whereis 搜索一个可执行工具及其相关配置、帮助 [rootberyl ~]# whereis ls ls: /bin/ls /usr/share/man/man1/ls.1.gz /usr/share/man/man1p/ls.1p.gz find […

计算机学报和c类会议,c类及以上学术期刊_c刊就是cssci吗_ccf推荐期刊和会议目录...

吃B族维生素片有什么好处大家族经常的成员有B1、B2、B3(烟酸)、B5(泛酸)、B6、B9(叶酸)、B12(钴胺素)。它们的作用如下。1&#xff0e;是糖代谢过程中关键性的物质。身体的肌肉和神经所需能量主要由糖类提供&#xff0c;所以最易受累。VB充足&#xff0c;则神经细胞能量充沛&am…

WPF ListBox样式去掉默认选中效果

次用到ListBox的时候&#xff0c;鼠标悬浮时&#xff0c;ListBoxItem的默认样式太丑了&#xff0c;设置了ItemTemplate也不管用&#xff0c;像这样的&#xff1a;经过几次尝试后&#xff0c;终于解决了这个问题&#xff0c;记录一下&#xff0c;以后就不用到处百度找了。。。其…

如何简单形象又有趣地讲解神经网络是什么?

神经网络很萌的&#xff01;0. 分类神经网络最重要的用途是分类&#xff0c;为了让大家对分类有个直观的认识&#xff0c;咱们先看几个例子&#xff1a;垃圾邮件识别&#xff1a;现在有一封电子邮件&#xff0c;把出现在里面的所有词汇提取出来&#xff0c;送进一个机器里&…

C#创建自定义特性

概述特性&#xff08;Attribute&#xff09;是用于在运行时传递程序中各种元素&#xff08;比如类、方法、结构、枚举、组件等&#xff09;的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号&#xff08…

win8计算机安全模式,WIN8如何设置按F8进入安全模式

Win8/win10系统想进入安全模式就不是太容易的事了。之前的Win7和XP等系统可以通过在开机时按F8进入安全模式选择菜单。有什么方法可以设置让Win8/win10系统按F8直接进入安全模式菜单。下面学习啦小编向大家介绍让Win8/win10系统通过这种老办法按F8进入安全模式的方法&#xff0…

当你不喜欢一个人(一段话、一篇文章)的时候

为什么80%的码农都做不了架构师&#xff1f;>>> 我知道技术人员都喜欢争论&#xff0c;因为我也算半个。不过我总觉得&#xff0c;有些争论是没有必要存在的。因为沟通是以交流为目的的&#xff0c;若本身对方就不是来交流而是做宣传的&#xff0c;推销一种观点。那…

floatmap 二维数组_用J中的多维数组进行Arrays.fill

用J中的多维数组进行Arrays.fill如何在不使用循环的情况下用Java填充多维数组&#xff1f; 我试过了&#xff1a;double[][] arr new double[20][4];Arrays.fill(arr, 0);结果为java.lang.ArrayStoreException: java.lang.Double提前致谢&#xff01;11个解决方案87 votes这是…