当您使用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查询。

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/

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

  1. linq查询不包含某个值的记录_mysql基本知识点梳理和查询优化

    作者:陈芳志来源:https://www.cnblogs.com/chenfangzhi/p/9979676.html 本文主要总结了工作中一些常用的操作及不合理的操作,在对慢查询进行优化时收集的一些 ...

  2. 【翻译】C#表达式中的动态查询

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

  3. linq查询不包含某个值的记录_MySQL行(记录)的详细操作

    阅读目录 一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理 一 介绍 MySQL数据操作: DML =========== ...

  4. mysql数据库中查询第几条到第几条数据_在 mysql 数据库中,从查询结果的第四条记录开始选取5条记录,下面 sql 语句正确的是( )...

    [单选题]同一种货物,在同一线路上或平行线路上作相对方向的运送,而与对方运程的全部或一部分发生重迭交错的运输被称为( ). [多选题]水闸一般由()三部分组成 [单选题]在一个常规的统计表内,非必需包 ...

  5. mysql查询第10到第20条记录_“取出数据表中第10条到第20条记录”的sql语句+selecttop用法...

    1.首先,select top用法: 参考问题 select top n * from和select * from的区别 select * from table -- 取所有数据,返回无序集合 sel ...

  6. .net函数查询_特来电智能分析平台动态查询架构创新实践

    一.业务背景及痛点 目前主流互联网智能分析平台中,数据查询作为基础的设施服务支撑着基础数据及业务分析的功能展现.随着数据量的增长,数据存储方式多元化,相对静态数据可能存储到关系型数据库中,订单类动态数 ...

  7. mysql 连接 分组_详解MySQL中的分组查询与连接查询语句

    分组查询 group by group by 属性名 [having 条件表达式][ with rollup] "属性名 "指按照该字段值进行分组:"having 条件表 ...

  8. mysql 连接查询分组_详解MySQL中的分组查询与连接查询语句

    分组查询 group bygroup by 属性名 [having 条件表达式][ with rollup] "属性名 "指按照该字段值进行分组:"having 条件表达 ...

  9. mysql 分组查询 语句_详解MySQL中的分组查询与连接查询语句

    分组查询 group bygroup by 属性名 [having 条件表达式][ with rollup] "属性名 "指按照该字段值进行分组:"having 条件表达 ...

最新文章

  1. 阿里敏捷实践| 4个迭代,从批量交付向持续交付转型
  2. 西门子S7以太网通讯协议
  3. iOS音频播放(一):概述
  4. 新星科技有限公司java_「新星科技」
  5. tcp连接工具_基于Swoole如何搭建TCP服务,你掌握了吗?
  6. tensorflow windows
  7. composer php 打包图片,composer 打包到 packagist
  8. mysql丢失数据_MySQL数据丢失问题
  9. Gartner2017年数据科学领域最酷供应商出炉,实至名归
  10. 伪装目标检测(Camouflage Object Detection)常用数据集
  11. linux中删除文件命令
  12. 2022华为杯数学建模A题思路代码
  13. oracle官网不同版本下载地址
  14. iOS16 中的 3 种新字体宽度样式
  15. python中round函数参数有负数_Python中round函数使用注意事项
  16. 利用Html与css从零开始制作基础静态网页(web课设)
  17. 第1章 数据库应用系统开发方法
  18. LeetCode代码刷题(17~24)
  19. Kalman Filter 通俗讲解
  20. 年度盘点丨2017十大最佳科技创新产品!

热门文章

  1. C# 8中的Async Streams
  2. Asp.Net Core 2.0 多角色权限认证
  3. 编写高性能 .NET 代码 第二章:垃圾回收 基本操作
  4. 未来的C#之只读引用与结构体
  5. 心电图计算心率公式_心电图到底能反应啥问题,看过之后你也能当“医生”
  6. 北京大学Tensorflow2.0笔记
  7. Android之项目全部能正常登录但是部分资源没有显示成功的解决办法
  8. mac之自己摸索的常用快捷键总结
  9. linux之ip route命令
  10. Android之sqlite常见用法以及取最新多少条数据(包括删除和不删除之外的数据)