当前位置: 首页 > news >正文

C# :IQueryable IEnumerable

文章目录

  • 1. IEnumerable
  • 2. IQueryable
  • 3. LINQ to SQL
  • 4. IEnumerable & IQueryable
    • 4.1 Expression
    • 4.2 Provider

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.Customerswhere 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.Customerswhere 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.lryc.cn/news/362096.html

相关文章:

  • 三、生成RPM包
  • 单实例11.2.0.4迁移到11.2.0.4RAC_使用rman异机恢复
  • MySQL之查询性能优化(二)
  • The Best Toolkit 最好用的工具集
  • 使用C#反射中的MAKEGENERICTYPE函数,来为泛型方法和泛型类指定(泛型的)类型
  • sql注入 (运用sqlmap解题)
  • HTML5 Canvas 绘图教程二
  • Linux 命令 find 的深度解析与使用
  • 字符串操作记录
  • 【python科学文献计量】关于中国知网检索策略的验证,以事故伤害严重程度检索为例
  • AdminController
  • Vue3-Pinia状态管理器
  • 安装存储器的段描述符并加载GDTR
  • 2024年5月架构试题
  • 品牌控价的同时也要做好数据分析
  • 微服务学习Day11-缓存问题学习
  • 虚拟化知识学习
  • 一键生成迷宫-Word插件-大珩助手新功能
  • 运维开发详解(上)
  • react useState基本使用
  • 基于jeecgboot-vue3的Flowable流程-待办任务(二)
  • 1103. 分糖果 II
  • SQL实验 数据的插入、修改和删除操作
  • es初始化
  • 【蓝桥杯】常见的数据结构
  • STM32H750外设ADC之外部触发和注入管理
  • 软设之排序算法对比
  • javascript导入excel文件
  • 「C系列」C 变量及常见问题梳理
  • <网络安全VIP>第二篇《工业控制软件》