使用NHibernate 3.2实现Repository(ORuM)(九)LINQ

Linq


LINQ(Language Integrated Query,语言集成查询),发音 "link",是一组技术的名称。LINQ是 Visual Studio 2008 和 .NET Framework 3.5 版中引入的一项创新功能,它在对象领域和数据领域之间架起了一座桥梁。LINQ 将强大的查询功能扩展到 C# 和 Visual Basic 的语言语法中,并采用标准的、易于学习的查询模式。可以对此技术进行扩展以支持几乎任何类型的数据存储。

传统上,针对数据的查询都是以简单的字符串表示,而没有编译时类型检查或 IntelliSense 支持。 此外,您还必须针对以下各种数据源学习一种不同的查询语言:SQL 数据库、XML 文档、各种 Web 服务等等。 LINQ 使查询成为 C# 和 Visual Basic 中的一流语言构造。 您可以使用语言关键字和熟悉的运算符针对强类型化对象集合编写查询。 下图显示了一个用 C# 语言编写的、不完整的 LINQ 查询,该查询针对 SQL Server 数据库,并具有完全类型检查和 IntelliSense 支持。 
 

在 Visual Studio 中,可以用 Visual Basic 或 C# 为以下数据源编写 LINQ 查询:SQL Server 数据库、XML 文档、ADO.NET 数据集,以及支持 IEnumerable 或泛型 IEnumerable<T> 接口的任意对象集合。 此外,还计划了对 ADO.NET Entity Framework 的 LINQ 支持,并且第三方为许多 Web 服务和其他数据库实现编写了 LINQ 提供程序。

LINQ 查询既可在新项目中使用,也可在现有项目中与非 LINQ 查询一起使用。 唯一的要求是项目应面向 .NET Framework 3.5 或更高版本。

LINQ 查询


查询是一种从数据源检索数据的表达式。 查询通常用专门的查询语言来表示。 随着时间的推移,人们已经为各种数据源开发了不同的语言;例如,用于关系数据库的 SQL 和用于 XML 的 XQuery。 因此,开发人员不得不针对他们必须支持的每种数据源或数据格式而学习新的查询语言。 LINQ 通过提供一种跨各种数据源和数据格式使用数据的一致模型,简化了这一情况。 在 LINQ 查询中,始终会用到对象。 可以使用相同的基本编码模式来查询和转换 XML 文档、SQL 数据库、ADO.NET 数据集、.NET 集合中的数据以及对其有 LINQ 提供程序可用的任何其他格式的数据。

所有 LINQ 查询操作都由以下三个不同的操作组成:
1.获取数据源。
2.创建查询。
3.执行查询。

 

LINQ查询表达式

查询表达式可用于查询和转换来自任意支持 LINQ 的数据源中的数据。 例如,单个查询可以从 SQL 数据库检索数据,并生成 XML 流作为输出。
查询表达式容易掌握,因为它们使用许多常见的 C# 语言构造。
查询表达式中的变量都是强类型的,但许多情况下您不需要显式提供类型,因为编译器可以推断类型。
在您循环访问 foreach 语句中的查询变量之前,不会执行查询。
在编译时,根据 C# 规范中设置的规则将查询表达式转换为“标准查询运算符”方法调用。 任何可以使用查询语法表示的查询也可以使用方法语法表示。 但是,在大多数情况下,查询语法更易读和简洁。
作为编写 LINQ 查询的一项规则,建议尽量使用查询语法,只在必需的情况下才使用方法语法。 这两种不同形式在语义或性能上没有区别。 查询表达式通常比用方法语法编写的等效表达式更易读。
一些查询操作,如 Count 或 Max,没有等效的查询表达式子句,因此必须表示为方法调用。 方法语法可以通过多种方式与查询语法组合。
查询表达式可以编译为表达式树或委托,具体取决于查询所应用到的类型。 IEnumerable<T> 查询编译为委托。 IQueryable 和 IQueryable<T> 查询编译为表达式树。

LINQ 查询操作中的类型关系


LINQ 查询操作在数据源、查询本身及查询执行中是强类型的。 查询中变量的类型必须与数据源中元素的类型和 foreach 语句中迭代变量的类型兼容。 此强类型保证在编译时捕获类型错误,以便可以在用户遇到这些错误之前更正它们。

不转换源数据的查询
下图演示不对数据执行转换的 LINQ to Objects 查询操作。 源包含一个字符串序列,查询输出也是一个字符串序列。 
 


1.数据源的类型参数决定范围变量的类型。
2.选择的对象的类型决定查询变量的类型。 此处的 name 为一个字符串。 因此,查询变量是一个 IEnumerable<string>。
3.在 foreach 语句中循环访问查询变量。 因为查询变量是一个字符串序列,所以迭代变量也是一个字符串。

 

转换源数据的查询
下图演示对数据执行简单转换的 LINQ to SQL 查询操作。 查询将一个 Customer 对象序列用作输入,并只选择结果中的 Name 属性。 因为 Name 是一个字符串,所以查询生成一个字符串序列作为输出。 
 


1.数据源的类型参数决定范围变量的类型。
2.select 语句返回 Name 属性,而非完整的 Customer 对象。 因为 Name 是一个字符串,所以 custNameQuery 的类型参数是 string,而非 Customer。
3.因为 custNameQuery 是一个字符串序列,所以 foreach 循环的迭代变量也必须是 string。
下图演示稍微复杂的转换。 select 语句返回只捕获原始 Customer 对象的两个成员的匿名类型。 
 


1.数据源的类型参数始终为查询中的范围变量的类型。
2.因为 select 语句生成匿名类型,所以必须使用 var 隐式类型化查询变量。
3.因为查询变量的类型是隐式的,所以 foreach 循环中的迭代变量也必须是隐式的。

 

让编译器推断类型信息
虽然您应该了解查询操作中的类型关系,但是您也可以选择让编译器为您执行全部工作。 关键字 var 可用于查询操作中的任何局部变量。
下图与前面讨论的第二个示例完全等效。 唯一的区别是编译器将为查询操作中的各个变量提供强类型: 
 

 

标准查询运算符


“标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法。 大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了 IEnumerable<T> 接口或 IQueryable<T> 接口。 标准查询运算符提供了包括筛选、投影、聚合、排序等功能在内的查询功能。
共有两组 LINQ 标准查询运算符,一组在类型为 IEnumerable<T> 的对象上运行,另一组在类型为 IQueryable<T> 的对象上运行。 构成每组运算符的方法分别是 Enumerable 和 Queryable 类的静态成员。 这些方法被定义为作为方法运行目标的类型的“扩展方法”。 这意味着可以使用静态方法语法或实例方法语法来调用它们。
此外,许多标准查询运算符方法运行所针对的类型不是基于 IEnumerable<T> 或 IQueryable<T> 的类型。 Enumerable 类型定义两个此类方法,这些方法都在类型为 IEnumerable 的对象上运行。 利用这些方法(Cast<TResult>(IEnumerable) 和 OfType<TResult>(IEnumerable)),您将能够在 LINQ 模式中查询非参数化或非泛型集合。 这些方法通过创建一个强类型的对象集合来实现这一点。 Queryable 类定义两个类似的方法(Cast<TResult>(IQueryable) 和 OfType<TResult>(IQueryable)),这些方法在类型为 Queryable 的对象上运行。
各个标准查询运算符在执行时间上有所不同,具体情况取决于它们是返回单一值还是值序列。 返回单一值的方法(例如 Average 和 Sum)会立即执行。 返回序列的方法会延迟查询执行,并返回一个可枚举的对象。
对于在内存中集合上运行的方法(即扩展 IEnumerable<T> 的那些方法),返回的可枚举对象将捕获传递到方法的参数。 在枚举该对象时,将使用查询运算符的逻辑,并返回查询结果。
与之相反,扩展 IQueryable<T> 的方法不会实现任何查询行为,但会生成一个表示要执行的查询的表达式树。 查询处理由源 IQueryable<T> 对象处理。
可以在一个查询中将对查询方法的调用链接在一起,这就使得查询的复杂性可能会变得不确定。

查询表达式语法
某些使用更频繁的标准查询运算符具有专用的 C# 和 Visual Basic 语言关键字语法,使用这些语法可以在“查询表达式”中调用这些运算符。

扩展标准查询运算符
通过创建适合于目标域或技术的特定于域的方法,您可以增大标准查询运算符的集合。 也可以用您自己的实现来替换标准查询运算符,这些实现提供诸如远程计算、查询转换和优化等附加服务。

 

LINQ 查询语法与方法语法


.NET 公共语言运行时 (CLR) 本身并不具有查询语法的概念,在编译时,查询表达式会转换为 CLR 确实了解的内容:方法调用。 这些方法称为“标准查询运算符”,它们具有如下名称:Where、Select、GroupBy、Join、Max、Average 等。可以通过使用方法语法而非查询语法来直接调用这些方法。
通常建议使用查询语法,因为它通常更简单、更易读;但是方法语法和查询语法之间并无语义上的区别。 此外,一些查询(如检索匹配指定条件的元素数的那些查询或检索具有源序列中的最大值的元素的查询)只能表示为方法调用。 System.Linq 命名空间中的标准查询运算符的参考文档通常使用方法语法。

class QueryVMethodSyntax
{
static void Main()
{
int[] numbers = { 5, 10, 8, 3, 6, 12};

//查询语法:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;

//方法语法:
IEnumerable<int> numQuery2 =
numbers.Where(num => num % 2 == 0)
.OrderBy(n => n);

foreach (int i in numQuery1)
{
Console.Write(i + "");
}
Console.WriteLine(System.Environment.NewLine);

foreach (int i in numQuery2)
{
Console.Write(i + "");
}
}
}
/*
输出:
6 8 10 12
6 8 10 12
*/

 

 表达式树


表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如一个方法调用或类似 x < y 的二元运算。
可编译和运行由表达式树所表示的代码。 通过这种方式,可动态修改可执行代码、执行各个数据库中的 LINQ 查询以及创建动态查询。
表达式树还可在动态语言运行时 (DLR) 中用来提供动态语言和 .NET Framework 之间的互操作性,并使编译器的编写器发出表达式树来替代 Microsoft 中间语言 (MSIL)。
可以基于匿名 lambda 表达式让 Visual C# 或 Visual Basic 编译器为您创建表达式树,也可以使用 System.Linq.Expressions 命名空间手动创建表达式树。

利用 Lambda 表达式创建表达式树
在将 lambda 表达式分配给 Expression<TDelegate> 类型的变量时,编译器将发出代码以生成一个表示 lambda 表达式的表达式树。
C# 和 Visual Basic 编译器仅可以从表达式 lambda(或单行 lambda)生成表达式树。 这两种编译器不能分析语句 lambda(或多行 lambda)。
下面的代码示例演示如何让 Visual C# 和 Visual Basic 编译器创建一个表示 lambda 表达式 num => num < 5 (C#) 或 Function(num) num < 5 (Visual Basic) 的表达式树。

Expression<Func<int, bool>> lambda = num => num < 5;


使用 API 创建表达式树


若要使用 API 创建表达式树,请使用 Expression 类。 此类包含创建特定类型的表达式树节点的静态工厂方法,例如,ParameterExpression(表示一个变量或参数)或 MethodCallExpression(表示一个方法调用)。 ParameterExpression、MethodCallExpression 以及其他表达式特定的类型也在 System.Linq.Expressions 命名空间中定义。 这些类型派生自抽象类型 Expression。
下面的代码示例演示如何使用 API 创建一个表示 lambda 表达式 num => num < 5 (C#) 或 Function(num) num < 5 (Visual Basic) 的表达式树。

ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
Expression.Lambda<Func<int, bool>>(
numLessThanFive,
new ParameterExpression[] { numParam });

 

源代码下载:http://mvcquick.codeplex.com/  


 

posted @ 2011-10-22 10:27  GuYoung  阅读(1213)  评论(0编辑  收藏  举报