C# :IQueryable IEnumerable

news2024/11/15 15:48:10

image.png

1. IEnumerable

namespace System.Collections:
public interface IEnumerable
{
  public IEnumerator GetEnumerator ();
}

public interface IEnumerator
{
    pubilc object Current { get; }
    public bool MoveNext ();
    public void Reset ();
}

IEnumerable 只有一个方法 GetEnumerator(), 既实现IEnumerable的所有泛型集合,都具备可枚举(IEnumerator)的能力

IEnumerator 只有一个属性 Current,和两个方法 MoveNext() \ Reset()

通过 MoveNext()Current 可以不停地移动 enumerator 的位置并返回当前的元素。

Reset() 会将 enumerator 设置到初始位置,既第一个元素之前

2. IQueryable

namespace System.Linq:

public interface IQueryable<out T> : System.Collections.Generic.IEnumerable<out T>, System.Linq.IQueryable {}

public interface IQueryable : System.Collections.IEnumerable
{
    // 表达式树返回结果的元素类型
    public Type ElementType { get; }

    // 获取IQueryable实例的表达式树
    public System.Linq.Expressions.Expression Expression { get; }

    public System.Linq.IQueryProvider Provider { get; }
}

// 定义用于创建和执行IQueryable对象所描述的查询的方法
public interface IQueryProvider
{
   public System.Linq.IQueryable CreateQuery(System.Linq.Expressions.Expression expression);

   public System.Linq.IQueryable<TElement> CreateQuery<TElement> (System.Linq.Expressions.Expression expression);

   public object? Execute(System.Linq.Expressions.Expression expression);

   public TResult Execute<TResult> (System.Linq.Expressions.Expression expression);
}

IQueryable 继承 IEnumerable,所以 IQueryable 具备可枚举的能力。

IQueryable 中的 Expession / Provider 则用来实现LINQ to SQL,具体可以看接下来的2节详细解释。

3. LINQ to SQL

LINQ to SQL是.Net Framework v3.5的组件,是能够提供将关系数据作为对象管理的运行时基础结构。

以往,编程语言通过 API 访问数据库数据的时候,需要将查询语句转为文本字符串。LINQ to SQL则会将对象模型中的语言集成查询转换为SQL,并发给数据库执行。当返回结果时,LINQ to SQL会再转换回可以用编程语言处理额对象。

所以,当拥有一个查询的时候,并不意味着查询已经执行:

var q = from c in dbContext.Customers Where c.City == ":London" select c;

命令对象会保留描述查询的字符串,IQueryable 对象的 Expression。命令对象的 ExecuteReader() 方法执行后,以 DataReader 形式返回结果。IQueryable 对象通过 GetEnumerator() 方法返回 IEnumerator 结果。

如下 foreach 会执行两次 query,这种行为成为延迟执行

var q =
   from c in db.Customers
   where c.City == "London"
   select c;
// Execute first time
foreach (Customer c in q)
   Console.WriteLine(c.CompanyName);
// Execute second time
foreach (Customer c in q)
   Console.WriteLine(c.CompanyName);

如果提前将结果转为任意标准的集合类,可以避免重复执行。

var q =
   from c in db.Customers
   where c.City == "London"
   select c;
// Execute once using ToList() or ToArray()
var list = q.ToList();
foreach (Customer c in list)
   Console.WriteLine(c.CompanyName);
foreach (Customer c in list)
   Console.WriteLine(c.CompanyName);

更多资料:LINQ:.NET Language-Integrated查询

4. IEnumerable & IQueryable

4.1 Expression

针对 IEnumerable所设计的扩展方法都将表达式视为委托,而针对 IQueryable 的那些扩展方法用的则是表达式树(expression tree)

IQueryable 会解析表达式树,并把这棵树表示的逻辑转为 provider 能够操作的格式,将其放在离数据最近的地方去执行。即传输数据往往会少于 IEnumerable,总体性能更好。

借鉴 《Effective C#》中的例子,如下两种写法,返回的结果相同,但是工作方式却不同:

// 1. use IQueryable
var q = from c in dbContext.Customers Where c.City == ":London" select c;
var finalAnswer = from c in q ordery c.Name select c;

// 2. use Enumerable
var q = (from c in dbContext.Customers where c.City == "London" select c).AsEnumerable();
var finaAnswer = from c in q orderby c.Name select c;

方法1,采用的是 IQueryable内置的 LINQ to SQL,q的查询语句,会和 第二行的组合起来,即只需要向数据库发送一次调用,where和orderby会在同一次sql查询操作里完成

方法2,则是把数据库对象从 IQueryable 强制转为了 IEnumerable形式的标准可枚举对象,即先向数据库发送查询请求,获得所有的数据后,放在本地进行排序操作

假如每种方法第二行还有一次 where 筛选一部分数据,那么方法1会组合2次 where,数据库只会返回最终目标数据集。而方法2会先从数据把所有第一次 where 得到的数据传到本地,之后在本地再次筛选,无疑增加了网络数据传输量。

4.2 Provider

IQueryable 的 Provider 未必能支持每一种查询方式,只能支持某些固定的运算符、方法,所以一旦查询操作里面调用除此之外的方法,那么就有可能把序列当成 IEnumerable 来查询,而非 IQueryable,否则会抛出异常。

再上 《Effective c#》例子:

private bool isValidProduct(Product p) => p.ProductName.LastIndexOf('C') == 0;

// 1. 转为 Enumerable 执行
var q1 = from p in dbContext.Products.AsEnumerable() where isValidProduct(p) select p;

// 2. 直接执行 IsValidProduct
var q2 = from p in dbContext.Products where isValidProduct(p) select p;

方法1可以正常运行,只是在 AsEnumerable() 之后,查询必须在本地执行,where 字句内的逻辑由LINQ to Objects处理。

方法2则会抛出异常,因为IQueryProvider会把查询操作转为T-SQL,交由远端执行。

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

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

相关文章

django使用fetch上传文件

在上一篇文章中&#xff0c;我包装了fetch方法&#xff0c;使其携带cookie。但是之前fetch传递的是json数据&#xff0c;现在有了一个上传文件的需求&#xff0c;因此需要进行修改&#xff1a; const sendRequest (url, method, data) > {const csrftoken Cookies.get(cs…

C++ | Leetcode C++题解之第119题杨辉三角II

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> getRow(int rowIndex) {vector<int> row(rowIndex 1);row[0] 1;for (int i 1; i < rowIndex; i) {row[i] 1LL * row[i - 1] * (rowIndex - i 1) / i;}return row;} };

HTML动态响应2-Servlet+Ajax实现HTTP前后台交互方式

作者:私语茶馆 前言 其他涉及到的参考章节: HTML动态响应1—Ajax动态处理服务端响应-CSDN博客 Web应用JSON解析—FastJson1.2.83/Tomcat/IDEA解析案例-CSDN博客 HTML拆分与共享方式——多HTML组合技术-CSDN博客 1.场景: WEb项目经常需要前后端交互数据,并动态修改HTML页…

洛谷 P1194 买礼物

题目来源于&#xff1a;洛谷 题目本质&#xff1a;图论生成树 代码如下&#xff1a; #include<bits/stdc.h> using namespace std; const int N10005; const int M250005; int n,m; int cnt,flag,px,py; int ans0; //ans表示最小花费的钱数 int f[N]; struct Edge{in…

list~模拟实现

目录 list的介绍及使用 list的底层结构 节点类的实现 list的实现 构造函数 拷贝构造 方法一&#xff1a;方法二&#xff1a; 析构函数 赋值重载 insert / erase push_/pop_(尾插/尾删/头插/头删) begin和end&#xff08;在已建立迭代器的基础上&#xff09; 迭代…

Windows【工具 06】mklink创建符号链接和硬链接(实现文件夹不同磁盘存储)

mklink创建符号链接和硬链接 1.创建符号链接1.1 目录符号链接1.2 文件符号链接 2.创建硬链接3.区别3.1 符号链接&#xff08;Symbolic Link&#xff09;3.2 硬链接&#xff08;Hard Link&#xff09; mklink是Windows中用于创建符号链接&#xff08;symbolic links&#xff09;…

数据库管理哪家强?Devart VS Navicat 360°全方位对比解析

今天我们向大家推荐的是两个开发环节的主流数据库管理品牌&#xff0c;那么你知道这两款数据库管理软件品牌与 数据库引擎配套的管理软件有什么区别吗&#xff1f;小编这就360全方位为您解答&#xff1a; ★ 品牌介绍 Devart&#xff1a;拥有超过20年的经验&#xff0c;利用最…

docker目录挂载失败:Check if the specified host path exists and is the expected type

docker目录挂载失败&#xff1a;Check if the specified host path exists and is the expected type docker目录挂载命令&#xff0c;其目的是为了达到修改linux上的文件同步到容器上&#xff0c;从而实现修改容器的配置文件。 在docker目录挂载或启动容器时报错&#xff0c…

若依前后端分离Spring Security新增手机号登录

备忘贴 转自&#xff1a;【若依RuoYi短信验证码登录】汇总_数据库_z_xiao_qiang-RuoYi 若依 配置Security: 按照Security的流程图可知&#xff0c;实现多种方式登录&#xff0c;只需要重写三个主要的组件&#xff0c;第一个用户认证处理过滤器&#xff0c;第二个用户认证tok…

【Git 版本管理】合并 + 变更,看懂Git

看懂 Git 合并操作分离 HEAD分离 HEAD 测试 相对引用(^ || ~)操作符 ^相对引用 ^ 测试操作符 ~相对引用 ~ 测试 撤销变更Git ResetGit Revert撤销变更 测试 整理提交记录Git Cherry-pick测试 交互式 rebase交互式 rebase 测试 合并操作 关键字&#xff1a;commit、branch、merg…

自媒体必用的50 个最佳 ChatGPT 社交媒体帖子提示prompt通用模板教程

在这个信息爆炸的时代&#xff0c;社交媒体已经成为我们生活中不可或缺的一部分。无论是品牌宣传、个人展示&#xff0c;还是日常交流&#xff0c;我们都离不开它。然而&#xff0c;要在众多信息中脱颖而出&#xff0c;吸引大家的关注并不容易。这时候&#xff0c;ChatGPT这样的…

DBeaver添加DM8驱动(maven下载和jar包下载配置)

DBeaver 24.0.3添加DM8驱动 下载DBeaver下载DM达梦驱动下载 安装配置使用自带Dameng自行添加达梦驱动 因为最近公司项目有信创要求&#xff0c;所以下载了达梦数据库。使用自带的达梦管理工具不是很方便&#xff0c;于是换了DBeaver。 哼哧哼哧安装好后&#xff0c;创建数据库连…

win11+vmware16.0+Ubuntu22.04+开机蓝屏

总结 本机系统 vm虚拟机下载 参考链接 1. 小白必看的Ubuntu20.04安装教程&#xff08;图文讲解&#xff09; 2. 软件目录【火星】——VM下载 3. Win11使用VMware15/16启动虚拟机直接蓝屏的爬坑记录 VMware16.0

MySQL(十二) Connector/C

首先可以在mysql的官网下载对应的库文件&#xff0c;这里就不演示了 1. 测试 通过 mysql_get_client_info() 函数&#xff0c;来验证我们的引入是否成功 #include <iostream> #include <mysql/mysql.h>int main() {std::cout << "mysql version:&quo…

帕友的锻炼小建议,助您重拾健康与活力

帕金森病&#xff0c;作为一种常见的神经系统退行性疾病&#xff0c;给患者的生活带来了诸多困扰。然而&#xff0c;通过科学的锻炼方法&#xff0c;我们可以有效缓解病情&#xff0c;提高生活质量。 一、有氧运动&#xff1a;提升心肺功能 对于帕金森病患者来说&#xff0c;适…

CentOS 7基础操作01_安装CentOS 7操作系统

1、实验环境 因为 Windows图形界面占用系统资源较高,所以公司准备将面向互联网的网站,数据库等重要应用基于Linux平台部署&#xff0c;并计划于近期将服务器安装开源免费的 CentOS 系统。进行前期准备工作时,需要公司的系统管理员尽快掌握 CentOS 系统的安装过程 2、需要描述 …

方差和标准差的区别

标准差和方差都是用来衡量随机变量的离散程度的统计量&#xff0c;它们之间有以下区别&#xff1a; 方差&#xff08;Variance&#xff09;&#xff1a; 方差是衡量随机变量离其均值的离散程度的度量。它是各个数据与其平均值之差的平方和的平均值。方差的公式为&#xff1a;…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt + uniapp 驾校预约平台 的设计与实现

一.项目介绍 系统角色&#xff1a;管理员、教练、学员 小程序(仅限于学员注册、登录)&#xff1a; 查看管理员发布的公告信息 查看管理员发布的驾校信息 查看所有教练信息、预约(需教练审核)、评论、收藏喜欢的教练 查看管理员发布的考试信息、预约考试(需管理…

javaweb基础之Ajax请求

大家好&#xff0c;这里是教授.F 目录 介绍&#xff1a; 原理图&#xff1a; 原生Ajax&#xff1a; JQuery的Ajax&#xff1a; 介绍&#xff1a; 是一种浏览器异步发起请求&#xff08;指定发那些数据&#xff09;&#xff0c;局部更新页面的技术 原理图&#xff1a; 原生…

MySQL -- SQL笔试题相关

1.银行代缴花费bank_bill 字段名描述serno流水号date交易日期accno账号name姓名amount金额brno缴费网点 serno: 一个 BIGINT UNSIGNED 类型的列&#xff0c;作为主键&#xff0c;且不为空。该列是自动增量的&#xff0c;每次插入新行时&#xff0c;都会自动递增生成一个唯一的…