目录
操作符和 LINQ
操作符类型
Lambda 表达式
First 和 Single
聚合、层次结构和投影
投影和排序
限定符和转换
结束语
语言集成查询 (LINQ) 允许开发人员通过强类型化语法使用 Microsoft® .NET Framework 3.5 代码编写类似 SQL 的查询。然后,各种 LINQ 提供程序,如 LINQ to Objects(可利用它根据对象层次结构编写查询)和 LINQ to Entities(可利用它根据实体框架的概念模型编写查询)可根据代表数据存储的细微差别来有效处理这些查询。
除强类型化语法外,LINQ 查询还具有一个标准查询操作符库来增强其功能。这些标准查询操作符对序列进行运算并可执行各种运算,如确定序列中是否存在某个值以及对序列运行合计函数(如求和)。
在本月的专栏中,我将使用 LINQ 来执行实际的查询和运算(会用到 LINQ to Objects 和 LINQ to Entities)。我将查询一个实体集合并使用其导航属性深入研究一组具备层次结构的实体。我还会为您演示如何对数组和集合应用多个标准查询操作符。并展示如何使用 lambda 表达式强化 LINQ 的标准查询操作符,以及如何利用它们来从序列解析特定信息并对序列执行复杂的逻辑运算。本专栏的下载中提供有所有代码示例(请参见 msdn.microsoft.com/msdnmag/code08.aspx)。
操作符和 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 类即可找到所有操作符)。以字母顺序显示了不同类型操作符的分类。可利用它来大致了解一下操作符所提供的功能。我将使用 LINQ to Objects 和 LINQ to Entities 展示一小组此类操作符,以显示它们如何为实际应用程序带来好处。
FigureACategories of Operators
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);
}
聚合、层次结构和投影
在 LINQ to Entities 查询中使用聚合操作符(如 Sum)可有助于简化查询。例如,以下代码检索订单总额大于 $10,000 的一个订单序列:
using (Entities entities = new Entities())
{
var query = from o in entities.Orders
where o.OrderDetails.Sum(
od => od.UnitPrice * od.Quantity) >= 10000
select o;
foreach (Orders order in query)
Console.WriteLine(order.OrderID);
}
由于 LINQ 可查询层次结构实体集合,因此标准查询操作符也可用于对嵌套实体序列执行运算。当必须计算或询问派生数据时,这一点非常有用。派生数据可能仅存在于其基本窗体中,如客户订单的详细信息仅包含单价和数量值。在本例中,未在模型中的任何位置提供代表订单总金额的聚合数据。然而,通过在 LINQ 查询中应用 Sum 操作符,仍可检索消费金额超过 $20,000 的所有客户,如下所示:
using (Entities entities = new Entities())
{
var query = from c in entities.Customers
where c.Orders.Sum(
o => o.OrderDetails.Sum(
od => od.UnitPrice * od.Quantity)) >= 25000
select c;
foreach (Customers customer in query)
Console.WriteLine(customer.CompanyName);
}
此示例展示了如何在 LINQ 查询的多个层次应用标准查询操作符。查询最终会返回一个 Customers 实体序列,但为达到此目的,它必须首先深入每个客户的订单以及每个订单的订单详细信息获取所需数据,这样才可以计算每项的价格,汇总每个订单的项目,然后汇总每个客户的总额。
3COME考试频道为您精心整理,希望对您有所帮助,更多信息在http://www.reader8.com/exam/