LINQ 的标准查询操作符
2008-03-31 09:32
288 查看
语言集成查询 (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 展示一小组此类操作符,以显示它们如何为实际应用程序带来好处。
Figure A Categories of Operators
Lambda 表达式
许多标准查询操作符在对序列执行运算时都使用 Func 委托来处理单个元素。Lambda 表达式可与标准查询操作符结合使用以代表委托。lambda 表达式是创建委托实现的简略表达形式,并可用于匿名委托适用的所有场合。C# 和 Visual Basic® .NET 均支持 Lambda 表达式。但是,必须注意:由于 Visual Basic .NET 尚不支持匿名方法,Lambda 表达式可能仅包含一个语句。
让我们来看看如何对一个整数数组使用 Single 操作符。这个整数数组的每个元素代表 2 的 1 到 10 次方。先创建此数组,然后使用 Single 操作符来检索满足 Lambda 表达式中指定条件的单个整数元素:
Lambda 表达式包含多个关键部分。Lambda 表达式首先定义传入委托的变量。在以上代码示例中,x(在 => 操作符左侧声明)是参数,代表传递给它的 nums 数组中的每个元素。Lambda 表达式的剩余部分代表数组中每个元素的评估逻辑。可使用匿名委托轻松地重新编写以上表达式,如下所示:
但是,此代码的可读性不及 Lambda 表达式。C# 2.0 引入了可使委托的传递稍微轻松些的匿名委托;但是,Lambda 表达式的简洁语法可使其更加简单。
First 和 Single
如果必须从序列中提取一个值,First、FirstOrDefault、Single 和 SingleOrDefault 操作符都非常有用。First 方法返回序列中的第一个元素。First 有一个重载方法,可使用它来传入 Lambda 表达式来代表一个条件。例如,如果要返回整数序列中整数元素大于 50 的第一个元素,可使用以下代码示例:
此代码会查找第一个元素 (1)、大于 50 的第一个元素 (64) 以及大于 5,000 的第一个元素。由于数组中没有元素满足第三个 Lambda 表达式(数组中无整数大于 5,000),则如果代码使用的是 First 操作符而非 FirstOrDefault,则会引发异常。在使用 FirstOrDefault 操作符时,如果没有元素满足 Lambda 表达式,则会返回 0。First 操作符也可用于 LINQ to Entities 查询,如下所示:
在此示例中,将返回 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 方法是个轻松的替代方法。)
除强类型化语法外,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 展示一小组此类操作符,以显示它们如何为实际应用程序带来好处。
Figure A Categories of Operators
操作符 | 说明 |
---|---|
聚合 | |
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 | 返回代表两个序列交集的序列 |
许多标准查询操作符在对序列执行运算时都使用 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); }
相关文章推荐
- LINQ标准查询操作符详解
- C#高级编程六十二天----LINQ标准的查询操作符
- LINQ 标准的查询操作符 分区 Take 、Skip 、TakeWhile 、SkipWhile
- LINQ定义的标准查询操作符
- LINQ 标准的查询操作符 排序 orderby、thenby、Take
- LINQ 标准的查询操作符 连接 join in on equals
- LINQ 标准的查询操作符 合计操作符 Count()、Sum()、Min()、Max()、Average()和Aggregate()
- LINQ入门教程之各种标准查询操作符(二)
- Linq--标准的查询操作符
- C#编程(六十二)---------LINQ标准的查询操作符
- LinQ标准的查询操作符 过滤where,index1,oftype
- LINQ 标准的查询操作符 合计操作符 Count()、Sum()、Min()、Max()、Average()和Aggregate()
- LINQ 标准的查询操作符 连接 join in on equals
- LINQ 标准的查询操作符 转换 ToList()、ToLookup()、ToEnumerable()、ToDictionary、ToType<T>
- LINQ 标准的查询操作符 生成操作符 Range()、Empty()和Repear()
- LINQ 标准的查询操作符 分组 group by into 、select new 、orderby descending、from in
- LINQ 的标准查询操作符
- LinQ标准的查询操作符 符合的from in子句
- LINQ 标准的查询操作符 转换 ToList()、ToLookup()、ToEnumerable()、ToDictionary、ToType
- LINQ之非延迟执行标准查询操作符(上)