LINQ 的标准查询操作符

语言集成查询 (LINQ) 允许开发人员通过强类型化语法使用 Microsoft® .NET Framework 3.5 代码编写类似 SQL 的查询。然后,各种 LINQ 提供程序,如 LINQ to Objects(可利用它根据对象层次结构编写查询)和 LINQ to Entities(可利用它根据实体框架的概念模型编写查询)可根据代表数据存储的细微差别来有效处理这些查询。
除强类型化语法外,LINQ 查询还具有一个标准查询操作符库来增强其功能。这些标准查询操作符对序列进行运算并可执行各种运算,如确定序列中是否存在某个值以及对序列运行合计函数(如求和)。
在本月的专栏中,我将使用 LINQ 来执行实际的查询和运算(会用到 LINQ to Objects 和 LINQ to Entities)。我将查询一个实体集合并使用其导航属性深入研究一组具备层次结构的实体。我还会为您演示如何对数组和集合应用多个标准查询操作符。并展示如何使用 lambda 表达式强化 LINQ 的标准查询操作符,以及如何利用它们来从序列解析特定信息并对序列执行复杂的逻辑运算。

操作符和 LINQ
LINQ 自身功能非常强大,无论使用的是 LINQ to XML、LINQ to DataSets、LINQ to Entities、LINQ to Objects 还是附带的任何其他 LINQ 提供程序。LINQ 的核心功能在于其强类型化查询语法,它可用于任意此类提供程序。当将 LINQ 与一个或多个标准查询操作符结合使用时,会得到一个功能更为强大的工具集,从而可精细地控制一组数据。
标准查询操作符在 System.Linq 命名空间中的 System.Core.dll 程序集中作为静态类 Enumerable 和 Queryable 的扩展方法存在,并且可用于实现 IEnumerable<T> 或 IQueryable<T> 的对象。这样它们就能使用 LINQ to Entities 和 LINQ to SQL 之类的提供程序对各类对象执行运算,从内存中的集合和数组(序列)到远程数据库。
可轻松地确定处理特定任务时所拥有的操作符。如果要在 LINQ 查询中使用操作符,可使用 Queryable 静态类可用扩展方法中的操作符。如果要对实现 IEnumerable<T> 的序列使用操作符,可使用 Enumerable 静态类中的一个扩展方法。但是,请记住:并非 Queryable 类中的所有操作符都适用于基础数据存储,因此运行时可能不支持某些操作符。

操作符类型
操作符有多种类型(使用对象浏览器查看 Enumerable 和 Queryable 类即可找到所有操作符)。图 A 以字母顺序显示了不同类型操作符的分类。可利用它来大致了解一下操作符所提供的功能。我将使用 LINQ to Objects 和 LINQ to Entities 展示一小组此类操作符,以显示它们如何为实际应用程序带来好处。

操作符 说明
聚合  
Aggregate 对序列执行一个自定义方法
Average 计算数值序列的平均值
Count 返回序列中的项目数(整数)
LongCount 返回序列中的项目数(长型)
Min 查找数字序列中的最小数
Max 查找数字序列中的最大数
Sum 汇总序列中的数字
连接  
Concat 将两个序列连成一个序列
转换  
Cast 将序列中的元素转换成指定类型
OfType 筛选序列中指定类型的元素
ToArray 从序列返回一个数组
ToDictionary 从序列返回一个字典
ToList 从序列返回一个列表
ToLookup 从序列返回一个查询
ToSequence 返回一个 IEnumerable 序列
元素  
DefaultIfEmpty 为空序列创建默认元素
ElementAt 返回序列中指定索引的元素
ElementAtOrDefault 返回序列中指定索引的元素,或者如果索引超出范围,则返回默认值
First 返回序列中的第一个元素
FirstOrDefault 返回序列中的第一个元素,或者如果未找到元素,则返回默认值
Last 返回序列中的最后一个元素
LastOrDefault 返回序列中的最后一个元素,或者如果未找到元素,则返回默认值
Single 返回序列中的单个元素
SingleOrDefault 返回序列中的单个元素,或者如果未找到元素,则返回默认值
相等  
SequenceEqual 比较两个序列看其是否相等
生成  
Empty 生成一个空序列
Range 生成一个指定范围的序列
Repeat 通过将某个项目重复指定次数来生成一个序列
分组  
GroupBy 按指定分组方法对序列中的项目进行分组
联接  
GroupJoin 通过归组将两个序列联接在一起
Join 将两个序列从内部联接起来
排序  
OrderBy 以升序按值排列序列
OrderByDescending 以降序按值排列序列
ThenBy 升序排列已排序的序列
ThenByDescending 降序排列已排序的序列
Reverse 颠倒序列中项目的顺序
分区  
Skip 返回跳过指定数目项目的序列
SkipWhile 返回跳过不满足表达式项目的序列
Take 返回具有指定数目项目的序列
TakeWhile 返回具有满足表达式项目的序列
投影  
Select 创建部分序列的投影
SelectMany 创建部分序列的一对多投影
限定符  
All 确定序列中的所有项目是否满足某个条件
Any 确定序列中是否有任何项目满足条件
Contains 确定序列是否包含指定项目
限制  
Where 筛选序列中的项目
设置  
Distinct 返回无重复项目的序列
Except 返回代表两个序列差集的序列
Intersect 返回代表两个序列交集的序列
Union 返回代表两个序列交集的序列

Lambda 表达式
许多标准查询操作符在对序列执行运算时都使用 Func 委托来处理单个元素。Lambda 表达式可与标准查询操作符结合使用以代表委托。lambda 表达式是创建委托实现的简略表达形式,并可用于匿名委托适用的所有场合。C# 和 Visual Basic® .NET 均支持 Lambda 表达式。但是,必须注意:由于 Visual Basic .NET 尚不支持匿名方法,Lambda 表达式可能仅包含一个语句。
让我们来看看如何对一个整数数组使用 Single 操作符。这个整数数组的每个元素代表 2 的 1 到 10 次方。先创建此数组,然后使用 Single 操作符来检索满足 Lambda 表达式中指定条件的单个整数元素:
int[] nums = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };
int singleNum = nums.Single(x => x > 16 && x < 64);
Console.WriteLine(singleNum.ToString());
Lambda 表达式包含多个关键部分。Lambda 表达式首先定义传入委托的变量。在以上代码示例中,x(在 => 操作符左侧声明)是参数,代表传递给它的 nums 数组中的每个元素。Lambda 表达式的剩余部分代表数组中每个元素的评估逻辑。可使用匿名委托轻松地重新编写以上表达式,如下所示:
int singleNum = nums.Single<int>(
delegate(int x) {return (x > 16 && x < 64); }
) ;
但是,此代码的可读性不及 Lambda 表达式。C# 2.0 引入了可使委托的传递稍微轻松些的匿名委托;但是,Lambda 表达式的简洁语法可使其更加简单。

First 和 Single
如果必须从序列中提取一个值,First、FirstOrDefault、Single 和 SingleOrDefault 操作符都非常有用。First 方法返回序列中的第一个元素。First 有一个重载方法,可使用它来传入 Lambda 表达式来代表一个条件。例如,如果要返回整数序列中整数元素大于 50 的第一个元素,可使用以下代码示例:
int[] nums = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };
int num1 = nums.First<int>();
int num2 = nums.First<int>(x => x > 50);
int num3 = nums.FirstOrDefault<int>(x => x > 5000);

Console.WriteLine(
num1.ToString() + "-" +
num2.ToString() + "-" +
num3.ToString());
此代码会查找第一个元素 (1)、大于 50 的第一个元素 (64) 以及大于 5,000 的第一个元素。由于数组中没有元素满足第三个 Lambda 表达式(数组中无整数大于 5,000),则如果代码使用的是 First 操作符而非 FirstOrDefault,则会引发异常。在使用 FirstOrDefault 操作符时,如果没有元素满足 Lambda 表达式,则会返回 0。First 操作符也可用于 LINQ to Entities 查询,如下所示:
using (Entities entities = new Entities())
{
var query = (from c in entities.Customers
select c).First(c => c.City.Equals("London"));
Console.WriteLine(query.CompanyName);
}
在此示例中,将返回 London 城中的第一个客户。正如您所看到的,当 First 方法用于各种 LINQ 提供程序(在本例中为 LINQ to Objects 和 LINQ to Entities)时,所用的语法并不会更改。
在 LINQ to Entities 上下文中,First 操作符非常有用,尤其是您知道会从查询返回单个记录时。例如,您可能有个查询,它常在给出 CustomerID 时获取一条客户记录。这种情况总是返回 0 或 1 条记录,因此,得到一个序列不如就得到一个实体本身。换句话说,您宁愿获取 Customer 实体而非 1 个 Customer 实体序列。First 方法在某种怦下非常有用,如以下代码段所示。(由于实体框架不会尝试在客户端和服务器之间分发单个查询的执行,并且 LINQ to Entities 不支持 Single 方法,因此使用 First 方法是个轻松的替代方法。)
using (Entities entities = new Entities())
{
var query = (from c in entities.Customers
where c.CustomerID.Equals("BOLID")
select c).First();
Console.WriteLine(query.CompanyName);
}

 
发布了22 篇原创文章 · 获赞 2 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览