【Entity Framework】聊聊EF中复杂查询运算符

【Entity Framework】聊聊EF中复杂查询运算符

文章目录

  • 【Entity Framework】聊聊EF中复杂查询运算符
    • 一、概述
    • 二、联接
    • 三、GroupJoin
    • 四、SelectMany
      • 4.1/集合选择器不引用外部
      • 4.2/集合选择器引用 where 子句中的外部
    • 五、GroupBy
    • 六、Left Join
    • 七、总结

一、概述

语言集成查询 (LINQ) 包含许多用于组合多个数据源或执行复杂处理的复杂运算符。 并非所有 LINQ 运算符都会在服务器端进行适当转换。 有时,采用一种形式的查询会转换为服务器,但如果采用另一种形式,即使结果相同,也不会转换。本文将介绍部分复杂运算符及其支持的变体。

二、联接

借助LINQ Join运算符,可根据每个源的键选择器连接两个数据源,并在键匹配时生成值的元组。该运算符在关系数据库中自然而然地转换为INNER JOIN。虽然LINQ Join具有外部和内部键选择器,但数据库只需要一个联接条件。因此EF Core通过比较外部键选择器和内部键选择器是否相等,来生成联接条件。

var query = from photo in context.Set<PersonPhoto>()join person in context.Set<Person>()on photo.PersonPhotoId equals person.PhotoIdselect new { person, photo };

下面语句生成SQL

SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON [p0].[PersonPhotoId] = [p].[PhotoId]

此外,如果键选择器是匿名类型,则 EF Core 会生成一个联接条件,以比较组件是否相等。

var query = from photo in context.Set<PersonPhoto>()join person in context.Set<Person>()on new { Id = (int?)photo.PersonPhotoId, photo.Caption }equals new { Id = person.PhotoId, Caption = "SN" }select new { person, photo };
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON ([p0].[PersonPhotoId] = [p].[PhotoId] AND ([p0].[Caption] = N'SN'))

三、GroupJoin

借助LINQ GroupJoin运算符,可以采用与Join类似的方式连接两个数据源,但它会创建一组内部值,用于匹配外部元素。执行与以下示例类似的查询将生成BlogIEnumerable<Post>的结果。由于数据库(特别是关系数据库)无法表示一组客户端对象,因此在许多情况下,GroupJoin 不会转换为服务器。 它需要从服务器获取所有数据来进行 GroupJoin,无需使用特殊选择器(下面的第一个查询)。 但如果选择器限制选定的数据,则从服务器提取所有数据可能会导致出现性能问题(下面的第二个查询)。 这就是 EF Core 不转换 GroupJoin 的原因。

var query = from b in context.Set<Blog>()join p in context.Set<Post>()on b.BlogId equals p.BlogId into groupingselect new { b, grouping };
var query = from b in context.Set<Blog>()join p in context.Set<Post>()on b.BlogId equals p.BlogId into groupingselect new { b, Posts = grouping.Where(p => p.Content.Contains("EF")).ToList() };

四、SelectMany

借助 LINQ SelectMany 运算符,可为每个外部元素枚举集合选择器,并从每个数据源生成值的元组。 在某种程度上,它是没有任何条件的联接,因此每个外部元素都与来自集合源的元素连接。 根据集合选择器与外部数据源的关系,SelectMany 可在服务器端转换为各种不同的查询。

4.1/集合选择器不引用外部

如果集合选择器不引用外部源中的任何内容,则结果是这两个数据源的笛卡尔乘积。 它在关系数据库中转换为 CROSS JOIN

var query = from b in context.Set<Blog>()from p in context.Set<Post>()select new { b, p };

生成SQL语句如下:

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
CROSS JOIN [Posts] AS [p]

4.2/集合选择器引用 where 子句中的外部

如果集合选择器具有引用外部元素的 where 子句,则 EF Core 会将其转换为数据库联接,并将谓词用作联接条件。 在对外部元素使用集合导航作为集合选择器时,通常会出现这种情况。 如果外部元素的集合为空,则不会为该外部元素生成任何结果。 但如果在集合选择器上应用 DefaultIfEmpty,则外部元素将与内部元素的默认值连接。 由于这种区别,应用 DefaultIfEmpty 时,如果缺少 DefaultIfEmptyLEFT JOIN,此类查询会转换为 INNER JOIN

var query = from b in context.Set<Blog>()from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId)select new { b, p };var query2 = from b in context.Set<Blog>()from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()select new { b, p };

生成SQL语句如下:

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]

五、GroupBy

LINQ GroupBy运算符创建IGrouping<TKey,TElement>类型的结果,其中TKeyTElement可以是任意类型,此外,IGrouping实现了IEnumerable<IElement>,这意味着可在分组后使用任意LINQ运算符来对其进行组合。由于任何数据库结构都无法表示IGrouping,因此在大多数情况下GroupBy运算符不会进行任何转换。聚合运算符应用于返回标量的每个组时,该运算符可在关系数据库中转换为 SQL GROUP BY。 SQL GROUP BY 也会受到限制。 它要求只按标量值进行分组。 投影只能包含分组键列或对列应用的任何聚合。 EF Core 标识此模式并将其转换为服务器,如以下示例中所示:

var query = from p in context.Set<Post>()group p by p.AuthorIdinto gselect new { g.Key, Count = g.Count() };
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]

EF Core 还会转换符合以下条件的查询:分组的聚合运算符出现在 Where 或 OrderBy(或其他排序方式)LINQ 运算符中。 它在 SQL 中将 HAVING 子句用于 where 子句。 在应用 GroupBy 运算符之前的查询部分可以是任何复杂查询,只要它可转换为服务器即可。 此外,将聚合运算符应用于分组查询以从生成的源中移除分组后,可以像使用任何其他查询一样,在它的基础上进行组合。

var query = from p in context.Set<Post>()group p by p.AuthorIdinto gwhere g.Count() > 0orderby g.Keyselect new { g.Key, Count = g.Count() };

生成SQL语句:

SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]

EF Core 支持的聚合运算符如下所示

序号.NETSQL
1Average(x => x.Property)AVG(Property)
2Count()Count(*)
3LongCount()Count(*)
4Max(x => x.Property)MAX(Property)
5Min(x => x.Property)MIN(Property)
6Sum(x => x.Property)SUM(Property)

可能支持其他聚合运算符。 检查提供程序文档以获取更多函数映射。

尽管没有表示 IGrouping 的数据库结构,但在某些情况下,EF Core 7.0 和更新版本可以在从数据库返回结果后创建分组。 这类似于 Include运算符在包含相关集合时的工作方式。 以下 LINQ 查询使用 GroupBy 运算符按 Price 属性的值对结果进行分组。

var query = context.Books.GroupBy(s => s.Price);

SQL语句执行:

SELECT [b].[Price], [b].[Id], [b].[AuthorId]
FROM [Books] AS [b]
ORDER BY [b].[Price]

在这种情况下,GroupBy 运算符不会直接转换为 SQL 中的 GROUP BY 子句,EF Core 会在从服务器返回结果后创建分组。

六、Left Join

虽然 Left Join 不是 LINQ 运算符,但关系数据库具有常用于查询的 Left Join 的概念。 LINQ 查询中的特定模式提供与服务器上的 LEFT JOIN 相同的结果。 EF Core 标识此类模式,并在服务器端生成等效的 LEFT JOIN。 该模式包括在两个数据源之间创建 GroupJoin,然后通过对分组源使用 SelectMany 运算符与 DefaultIfEmpty 来平展分组,从而在内部不具有相关元素时匹配 null。 下面的示例显示该模式的样式及其生成的内容。

var query = from b in context.Set<Blog>()join p in context.Set<Post>()on b.BlogId equals p.BlogId into groupingfrom p in grouping.DefaultIfEmpty()select new { b, p };

七、总结

以上模式在表达式树中创建复杂的结构。因此,EF Core要求在紧随运算符的步骤中将GroupJoin运算符的分组结果平展。即使使用GroupJoin-DefaultIfEmpty-SelectMany,但采用其他的模式,也不能将其标识为Left Join

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

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

相关文章

使用Xterm实现终端构建

————html篇———— // 需要使用Xterm Xterm的官网&#xff1a; Xterm.js 新建项目 增加基本文件 下载 框架 npm init -y Xterm依赖 npm install xterm/xterm 参考文档写的代码 贴入代码 <html><head><link rel"stylesheet" href"nod…

免费思维13招之十三:种群型思维

免费思维13招之十三&#xff1a;种群型思维 免费思维的最后一个思维——族群思维 人&#xff0c;都是群居性的动物&#xff0c;在人群中的一部分人群对于另一部分人群来说&#xff0c;具有强大的吸引力。那么&#xff0c;我们就从这一点出发&#xff0c;通过对其中一部分人群进…

知识付费系统开发成本高吗,大学生有哪些可以网络授课的平台?你知道吗?

在线教育绝对是以后教育行业发展的方向&#xff0c;越来越多发线下机构慢慢的向这方面去开始转型&#xff0c;做网上教育。那么大学生有哪些可以网络授课的平台?你知道吗? 软件类的网上授课平台 如钉钉&#xff0c;飞书等&#xff0c;这些软件都是由网上授课功能的&#xff0…

2万字实操入门案例之在Springboot框架下用Mybatis简化JDBC开发实现基础的操作MySQL之预编译SQL主键返回增删改查

环境准备 准备数据库表 use mybatis;-- 部门管理 create table dept(id int unsigned primary key auto_increment comment 主键ID,name varchar(10) not null unique comment 部门名称,create_time datetime not null comment 创建时间,update_time datetime not null comme…

Idea + maven 搭建 SSH (struts2 +hibernate5 + spring5) 环境

org.apache.struts struts2-core 2.3.35 org.apache.struts struts2-spring-plugin 2.3.35 org.apache.struts struts2-json-plugin 2.3.8 1.4 配置Java EE 坐标依赖 这里可以引入 servlet api&#xff0c;jstl 标签库等一系列工具 javax.servlet javax.servlet-api …

6大部分,20 个机器学习算法全面汇总!!建议收藏!(上篇)

前两天有小伙伴说想要把常见算法的原理 公式汇集起来。 这样非常非常方便查看&#xff01;分为上下两篇&#xff0c;下篇地址&#xff1a; 本次文章分别从下面6个方面&#xff0c;涉及到20个算法知识点&#xff1a; 监督学习算法 无监督学习算法 半监督学习算法 强化学习…

YOLOV8环境部署(GPU版本)

一、安装&#xff43;&#xff55;&#xff44;&#xff41;和&#xff43;&#xff55;&#xff44;&#xff4e;&#xff4e; 1、安装cuda之前先打开英伟达控制面板查看自己的显卡信息 2、“帮助”—>“系统信息”—>“组件”&#xff0c;然后看第三行的信息“Nvidia …

进程信号 signal

文章目录 信号基础信号的产生OS中的时间 信号的保存sigset_tsigprocmasksigpending 信号的捕捉用户态和内核态sigactionvolatile SIGCHLD 信号基础 生活中的信号 你在网上买了很多件商品&#xff0c;再等待不同商品快递的到来。但即便快递没有到来&#xff0c;你也知道快递来临…

elememt-plus的表格的增删改查#Vue3无需json数据,无需后端接口

elememt-plus的表格的增删改查#Vue3无需json数据&#xff0c;无需后端接口 实现效果&#xff1a; <template><!-- 演示地址 --><div class"dem-add"><!-- Search start --><div class"dem-title"><p>演示地址</…

Python装饰器:深入理解与应用实例

Python装饰器&#xff1a;深入理解与应用实例 一、引言 在Python编程中&#xff0c;装饰器&#xff08;Decorators&#xff09;是一个强大且实用的特性&#xff0c;它允许程序员在不修改原有函数或类代码的情况下&#xff0c;为其添加额外的功能。装饰器本质上是一个可调用对…

基于springboot的知识管理系统源码数据库

基于springboot的知识管理系统源码数据库 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#xff0c;针对这个问题开发一个专门适应师生作业交流形式的网站。本文介绍了知识管理系统的开发全过程。通过分析企业对于知识管理系统的需求&#xff0c;创建了…

【Git教程】(十八)拆分大项目 — 概述及使用要求,执行过程及其实现,替代解决方案 ~

Git教程 拆分大项目 1️⃣ 概述2️⃣ 使用要求3️⃣ 执行过程及其实现3.1 拆分模块版本库3.2 将拆分出的模块作为外部版本库集成 4️⃣ 替代解决方案 通常软件项目都是由单体小型系统开始的&#xff0c;在开发过程中项目规模和团队人员不断扩大&#xff0c; 将项目模块化会显得…

C#实现各种Hash计算

C#实现各种Hash计算 文章目录 C#实现各种Hash计算涉及框架及库目前支持可计算的类型核心代码完整可运行代码 BCrypt总结 涉及框架及库 自己在NuGet管理器里面安装即可 BouncyCastle.Cryptography&#xff1a;是加密算法和协议的.NET实现。 目前支持可计算的类型 BLAKE2B_16…

如何在Idea离线情况下安装vue.js插件

亲践有效&#xff0c;步骤如下: 1. 互联网环境登陆vue.js官网(Vue.js - IntelliJ IDEs Plugin | Marketplace)。 2. 然后先确定你的IDEA的版本&#xff1a;在你IDEA的安装文件中找到product-info.json&#xff0c;里面的buildNumber记录着你IDEA的精确版本号&#xff0c;根据…

【Entity Framework】EF中SaveChanges如何使用

【Entity Framework】EF中SaveChanges如何使用 文章目录 【Entity Framework】EF中SaveChanges如何使用一、概述二、更改跟踪和SaveChanges三、SaveChanges优势四、使用SaveChanges添加数据五、使用SaveChanges更新数据六、使用SaveChanges删除数据七、单个SaveChanges中的多个…

roscore启动报错的解决方法【将环境变量配置于最后】

今天在启动rviz时发生一个很奇怪的报错&#xff1a; rviz: error while loading shared libraries: librviz.so: cannot open shared object file: No such file or directory 我感觉很纳闷&#xff01;再试着启动一下roscore&#xff0c;发现如下报错&#xff1a; [rosout-1…

[muduo网络库]——使用muduo库搭建Echo服务器(剖析muduo网络库核心部分、设计思想)

在此之前&#xff0c;我们对于muduo库的每一类几乎都进行了逐行的分析&#xff0c;但是一个网络库的每个模块之间总是有千丝万缕的关系&#xff0c;所以可能有的地方还是有分析的不到位&#xff0c;所以从这一篇开始&#xff0c;我们从muduo的简单使用----搭建一个Echo服务器&a…

Python爬虫从入门到精通:一篇涵盖所有细节的高质量教程

目录 第一部分&#xff1a;Python爬虫基础 1.1 爬虫原理 1.2 Python爬虫常用库 1.3 爬虫实战案例 1.4 注意事项 第二部分&#xff1a;爬虫进阶技巧 2.1 处理动态加载的内容 2.2 登录认证 2.3 分布式爬取 2.4 反爬虫策略 第三部分&#xff1a;爬虫实战项目 3.1 豆瓣…

【C语言】指针(二)

目录 一、传值调用和传址调用 二、数组名的理解 三、通过指针访问数组 四、一维数组传参的本质 五、指针数组 六、指针数组模拟实现二维数组 一、传值调用和传址调用 指针可以用在哪里呢&#xff1f;我们看下面一段代码&#xff1a; #include <stdio.h>void Swap(i…

基于Spring封装一个websocket工具类使用事件发布进行解耦和管理

最近工作中&#xff0c;需要将原先的Http请求换成WebSocket&#xff0c;故此需要使用到WebSocket与前端交互。故此这边需要研究一下WebSocket到底有何优点和不可替代性&#xff1a; WebSocket优点&#xff1a; WebSocket 协议提供了一种在客户端和服务器之间进行全双工通信的…