C# :IQueryable IEnumerable

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,交由远端执行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【PHP项目实战训练】——laravel框架的实战项目中可以做模板的增删查改功能(1)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

UE5 Http Server

前言 最近要用UE 作为一个服务器去接收来自外部的请求&#xff0c;从而在UE中处理一些内容&#xff0c;但是之前只做过请求&#xff0c;哪整过这玩意&#xff0c;短期内还得出结果&#xff0c;那怎么搞嘞&#xff0c;本着省事的原则就找找呗&#xff0c;有没有现成的&#xff0…

2020 6.s081——Lab5:Lazy page allocation

再来是千年的千年 不变是眷恋的眷恋 飞越宇宙无极限 我们永不说再见 ——超兽武装 完整代码见&#xff1a;SnowLegend-star/6.s081 at lazy (github.com) Eliminate allocation from sbrk() (easy) 顾名思义&#xff0c;就是去掉sbrk()中调用growproc()的部分。1s完事儿。 Laz…

两数之和 II - 输入有序数组,三数之和

题目一&#xff1a; 代码如下&#xff1a; vector<int> twoSum(vector<int>& numbers, int target) {int left 0;int right numbers.size() - 1;vector<int> ret;while (left < right){int tmp numbers[left] numbers[right];if (tmp target){r…

Mac OS 用户开启 80 端口

开启端口 sudo vim /etc/pf.conf # 开放对应端口 pass out proto tcp from any to any port 8080 # 刷新配置文件 sudo pfctl -f /etc/pf.conf sudo pfctl -e获取本机ip地址 ifconfig en0 | grep inet | grep -v inet6 | awk {print $2}访问指定端口

栈和队列题目练习

本节小编选了两道题来加深对栈和队列的认识理解&#xff01; 有效的括号 方法1&#xff1a;直接用栈的结构&#xff08;动态数组&#xff09; 本题可以用栈这个结构来解答&#xff0c;将(,{,[ 左括号压入栈中&#xff0c;然后取出栈顶元素与右括号),},]匹配。不匹配的话&…

【成品设计】基于STM32的智能婴儿床设计

《基于STM32的智能婴儿床设计》 所需器件&#xff1a; 主控&#xff1a;STM32F103C8T6最小系统板。OLED屏幕&#xff1a;显示系统状态等。按键&#xff1a;自动模式和遥控模式切换 。180度舵机模块&#xff1a;通过0度~90度之间摆动模拟婴儿床的摆动。360度舵机模块&#xff…

为什么要使用动态代理IP?

一、什么是动态代理IP&#xff1f; 动态代理IP是指利用代理服务器来转发网络请求&#xff0c;并通过不断更新IP地址来保护访问者的原始IP&#xff0c;从而达到匿名访问、保护隐私和提高访问安全性的目的。动态代理IP在多个领域中都有广泛的应用&#xff0c;能够帮助用户…

函数调用之栈平衡

一&#xff0c;前言 如约而至&#xff0c;献上c/c在调用函数过程中关于栈平衡的心得&#xff0c;帮助大家了解内存中关于栈空间的分配过程&#xff08;ps:栈平衡通常也被说成堆栈平衡&#xff09;&#xff1b; 话不多说&#xff0c;下面以函数 int __cdecl GetResult(int uPa…

【康耐视国产案例】AI视觉相机创新 加速商超物流数智化转型

连锁商超/零售店正面临着因消费者购物习惯改变等挑战&#xff0c;迎来了以新兴技术崛起而催生的数字化物流体系转型需求。物流行业与AI机器视觉的深度融合&#xff0c;解决了传统机器视觉识别速度慢、环境要求高、定制化部署耗时过多等痛点&#xff0c;大大提高了物流供应链的效…

NPDP(New Product Development Professional)

NPDP&#xff08;New Product Development Professional&#xff09; NPDP考试介绍 NPDP证书介绍

邮件大附件发送失败影响业务?看看500强企业是如何解决的

邮箱业务往来对于企业来说是最常见的&#xff0c;因为其便捷性和普遍性&#xff0c;沟通使用成本也是最低的&#xff0c;在多种邮箱中&#xff0c;Outlook邮箱因其专业简洁的使用体验&#xff0c;在全世界范围内被企业广泛使用。 企业邮箱业务往来少不了附件的使用&#xff0c;…

【记忆化搜索 】2312. 卖木头块

本文涉及知识点 记忆化搜索 LeetCode2312. 卖木头块 给你两个整数 m 和 n &#xff0c;分别表示一块矩形木块的高和宽。同时给你一个二维整数数组 prices &#xff0c;其中 prices[i] [hi, wi, pricei] 表示你可以以 pricei 元的价格卖一块高为 hi 宽为 wi 的矩形木块。 每…

Python魔法之旅-魔法方法(03)

目录 一、概述 1、定义 2、作用 二、主要应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类…

SpringMVC框架学习笔记(三):url请求风格-Rest 以及 SpringMVC 映射获取到各种类型数据

1 Rest 基本介绍 1.1 基本说明 REST&#xff1a;即 Representational State Transfer。(资源)表现层状态转化。是目前流行的请求方 式。它结构清晰, 很多网站采用 HTTP 协议里面&#xff0c;四个表示操作方式的动词&#xff1a;GET、POST、PUT、DELETE。它们分别对应四种基本…

Docker 私有仓库部署和管理

目录 一、案例一 概述 二、案例一 前置知识点 2.1、什么是 Docker Compose 2.2、什么是 Consul 三、案例一 使用 docker Compose 搭建 Consul 集群环境 3.1、案例实验环境 3.2、案例需求 四、案例实施 4.1、Docker 网络通信 1&#xff09;端口映射 2&#xf…

SpringMVC响应数据 View

1.如何封装数据返回页面 使用ModelAndView&#xff1a; ModelAndView modelAndView new ModelAndView() modelAndView.addObject() 方法封装数据 使用Controller中内置Model对象 model&#xff1a; model.addAttribute("name","zz"); 2.跳转的方式…

上位机图像处理和嵌入式模块部署(f407 mcu原理图)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们说过&#xff0c;和103相比较&#xff0c;407速度更快、频率更高&#xff0c;而且资源更多&#xff0c;当然可以做的事情也就更多。此外&a…

【小白专用 已验证24.5.30】ThinkPHP6 视图

ThinkPHP6 视图 模板引擎支持普通标签和XML标签方式两种标签定义&#xff0c;分别用于不同的目的 标签类型描述普通标签主要用于输出变量、函数过滤和做一些基本的运算操作XML标签也称为标签库标签&#xff0c;主要完成一些逻辑判断、控制和循环输出&#xff0c;并且可扩展 c…

如何评价GPT-4o?GPT-4o和ChatGPT4.0的区别是啥呢?

如何评价GPT-4o? GPT-4o代表了人工智能领域的一个重要里程碑&#xff0c;它不仅继承了GPT-4的强大智能&#xff0c;还在多模态交互方面取得了显著进步。以下是几个方面的分析&#xff1a; 技术特点 多模态交互能力&#xff1a;GPT-4o支持文本、音频和图像的任意组合输入与输出…