一.什么是LINQ
LINQ是 .NET 3.0 版中引入的一项创新功能。LINQ(读音link)表示语言集成查询(Language Integrated Query),是.NET框架的扩展,它允许我们用SQL查询数据库的方式来查询数据的集合,使用它,你可以使用统一的方式从数据库、程序对象的集合以及XML文档中查询数据.
二.为什么要使用LINQ
要理解为什么使用LINQ,先来看下面一个例子。“假设有一个整数类型的数组,找到里面的偶数并进行降序排序。”
在C#3.0以前,如果要实现这样的功能,我们必须使用’foreach’或’for’循环来遍历数组,先找到偶数然后在降序排序,相关代码如下:
class Program
{
static void Main(string[] args)
{
// 查询出数组中的偶数并排序
int[] ints = {
5, 2, 0, 66, 4, 32, 7, 1 };
// 定义一个整数类型的集合,用来存放数组中的偶数
List<int> list = new List<int>();
// 遍历数组查询出偶数放到集合中
foreach (int i in ints)
{
// 如果是偶数,把偶数加入到集合中
if (i % 2 == 0) list.Add(i);
}
// 正序排序
list.Sort();
// 反转
list.Reverse();
// 输出
Console.WriteLine(string.Join(",",list));
}
}
C#3.0之后引入了新特性LINQ,下面的示例演示了如何使用LINQ来查询数组,示例代码如下:
class Program
{
static void Main(string[] args)
{
// 查询出数组中的奇数并排序
int[] ints = {
5, 2, 0, 66, 4, 32, 7, 1 };
// 使用LINQ和Lambda表达式查询数组中的偶数
int[] intEvens= ints.Where(p => p % 2 == 0).ToArray();
// 输出
Console.WriteLine("偶数:" + string.Join(",", intEvens));
Console.WriteLine("奇数:" + string.Join(",", intOdds));
}
}
在上面的例子中可以看到,我们在单个语句中使用LINQ和Lambda表达式指定不同的查询条件,因此,LINQ使代码更加紧凑和可读,并且它也可以用于查询不同的数据源。
三.LINQ的优点
-
1.linq可以用最少的代码对集合执行查询,筛选、排序和分组操作。
-
2.统一查询语句
长期以来,开发社区形成以下的格局:
1、面向对象与数据访问两个领域长期分裂,各自为政。
2、编程语言中的数据类型与数据库中的数据类型形成两套不同的体系,例如:C#中字符串用string数据类型表示。SQL中字符串用NVarchar/Varchar/Char数据类型表示。
3、SQL编码体验落后,没有智能感知效果,没有严格意义上的强类型和类型检查。
4、SQL和XML都有各自的查询语言,而对象没有自己的查询语言。
上面描述的问题,都可以使用LINQ解决。LINQ使你可以使用统一的方式编写各种查询。用于保存和检索来自不同数据源的数据,从而消除了编程语言和数据库之间的不匹配,以及为不同类型的数据源提供单个查询接口。
四.LINQ的功能
LINQ 系列技术提供了针对对象 (LINQ to Objects)、关系数据库 (LINQ to SQL) 和 XML (LINQ to XML) 的一致查询体验。
但是**语言集成查询 (LINQ) **不只是查询数据。 它也是用于转换数据的强大工具。 通过使用 LINQ 查询,可以使用源序列作为输入,并通过多种方式对其进行修改,以创建新的输出序列。 通过排序和分组,你可以修改序列本身,而无需修改这些元素本身。 但也许 LINQ 查询最强大的功能是创建新类型。 这可以在 select 子句中完成。 例如,可以执行下列任务:
- 将多个输入序列合并为具有新类型的单个输出序列。
- 创建其元素由源序列中每个元素的一个或多个属性组成的输出序列。
- 创建其元素由对源数据执行的操作结果组成的输出序列。
- 创建其他格式的输出序列。 例如,可以将数据从 SQL 行或文本文件转换为 XML。
这只是几个例子。 当然,可以以各种方式在同一查询中组合这些转换。 此外,一个查询的输出序列可以用作新查询的输入序列。
五.LINQ查询操作
有 LINQ 查询操作都由以下三个不同的操作组成:
- 获取数据源。
- 创建查询。
- 执行查询。
class IntroToLINQ
{
static void Main()
{
// The Three Parts of a LINQ Query:
// 1. 获取数据源.
int[] numbers = new int[7] {
0, 1, 2, 3, 4, 5, 6 };
// 2. 创建查询.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. 执行查询.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
}
}
下图演示完整的查询操作。 在 LINQ 中,查询的执行不同于查询本身。 换句话说,仅通过创建查询变量不会检索到任何数据。
1.数据源
上例中,数据源是一个数组,因此它隐式支持泛型 IEnumerable 接口。 我们称 支持 IEnumerable 或派生接口(如泛型 IQueryable)的类型称为可查询类型 。
2.创建查询语句
查询指定要从数据源中检索的信息。 查询还可以指定在返回这些信息之前如何对其进行排序、分组和结构化。 查询存储在查询变量中,并用查询表达式进行初始化。 为使编写查询的工作变得更加容易,C# 引入了新的查询语法。
上一个示例中的查询从整数数组中返回所有偶数。 该查询表达式包含三个子句:from
、where
和 select
。 (如果熟悉 SQL,会注意到这些子句的顺序与 SQL 中的顺序相反。)from
子句指定数据源,where
子句应用筛选器,select
子句指定返回的元素的类型。 语言集成查询 (LINQ) 一节中详细讨论了这些子句和其他查询子句。 目前需要注意的是,在 LINQ 中,查询变量本身不执行任何操作并且不返回任何数据。 它只是存储在以后某个时刻执行查询时为生成结果而必需的信息。 有关在后台如何构造查询的详细信息,请参阅标准查询运算符概述 (C#)。
4.查询执行
a.延迟执行
如前所述,查询变量本身只存储查询命令。 查询的实际执行将推迟到在 foreach
语句中循环访问查询变量之后进行。 此概念称为 延迟执行,下面示例:
// Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
foreach
语句也是检索查询结果的地方。 例如,在上一个查询中,迭代变量 num
保存了返回的序列中的每个值(一次保存一个值)。
由于查询变量本身从不保存查询结果,因此可以根据需要随意执行查询。 例如,可以通过一个单独的应用程序持续更新数据库。 在应用程序中,可以创建一个检索最新数据的查询,并可以按某一时间间隔反复执行该查询以便每次检索不同的结果。
b.强制立即执行
对一系列源元素执行聚合函数的查询必须首先循环访问这些元素。 Count
、Max
、Average
和 First
就属于此类查询。 由于查询本身必须使用 foreach
以便返回结果,因此这些查询在执行时不使用显式 foreach
语句。 另外还要注意,这些类型的查询返回单个值,而不是 IEnumerable
集合。 下面的查询返回源数组中偶数的计数:
var evenNumQuery =
from num in numbers
where (num % 2) == 0
select num;
int evenNumCount = evenNumQuery.Count();
要强制立即执行任何查询并缓存其结果,可调用 ToList 或 ToArray 方法。
List<int> numQuery2 =
(from num in numbers
where (num % 2) == 0
select num).ToList();
// or like this:
// numQuery3 is still an int[]
var numQuery3 =
(from num in numbers
where (num % 2) == 0
select num).ToArray();
此外,还可以通过在紧跟查询表达式之后的位置放置一个 foreach
循环来强制执行查询。 但是,通过调用 ToList
或 ToArray
,也可以将所有数据缓存在单个集合对象中。
六.LINQ 中的查询语法和方法语法
一般介绍LINQ的文档中的大多数查询是使用 LINQ 声明性查询语法编写的。 但是在编译代码时,查询语法必须转换为针对 CLR 的方法调用。 这些方法调用会调用标准查询运算符(名称为 Where
、Select
、GroupBy
、Join
、Max
和 Average
等)。 可以使用方法语法(而不是查询语法)来直接调用它们。查询语法和方法语法在语义上是相同的。
下面的示例演示一个简单的查询语法和方法语法等效的查询:
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 + " ");
}
}
}
/*
Output:
6 8 10 12
6 8 10 12
*/
七.查询语法关键字
查询表达式中使用的上下文关键字。
示例代码:
// 查询表达式语法.
var query = from word in words
group word.ToUpper() by word.Length into gr
orderby gr.Key
select new {
Length = gr.Key, Words = gr };
子句 | 说明 |
---|---|
from | 指定数据源和范围变量(类似于迭代变量)。 |
where | 基于由逻辑 AND 和 OR 运算符(&& 或 || )分隔的一个或多个布尔表达式筛选源元素。 |
select | 指定执行查询时,所返回序列中元素的类型和形状。 |
group | 根据指定的密钥值对查询结果分组。 |
into | 提供可作为对 join、group 或 select 子句结果引用的标识符。 |
orderby | 根据元素类型的默认比较器对查询结果进行升序或降序排序。 |
join | 基于两个指定匹配条件间的相等比较而联接两个数据源。 |
let | 引入范围变量,在查询表达式中存储子表达式结果。 |
in | join 子句中的上下文关键字。 |
on | join 子句中的上下文关键字。 |
equals | join 子句中的上下文关键字。 |
by | group 子句中的上下文关键字。 |
ascending | orderby 子句中的上下文关键字。 |
descending | orderby 子句中的上下文关键字。 |
八.方法语法运算符
标准查询运算符 是组成 LINQ 模式的方法。 这些方法中的大多数都作用于序列;其中序列指其类型实现 IEnumerable 接口或 IQueryable 接口的对象。 标准查询运算符提供包括筛选、投影、聚合、排序等在内的查询功能。
示例代码:
// 使用标准方法查询语法
var query = words.
GroupBy(w => w.Length, w => w.ToUpper()).
Select(g => new {
Length = g.Key, Words = g }).
OrderBy(o => o.Length);
查询表达式语法表
下表列出包含等效查询表达式子句的标准查询运算符。
方法 | C# 查询表达式语法 |
---|---|
Cast | 使用显式类型化范围变量,例如: from int i in numbers (有关详细信息,请参阅 from 子句。) |
GroupBy | group … by - 或 - group … by … into … (有关详细信息,请参阅 group 子句。) |
GroupJoin(IEnumerable, IEnumerable, Func, Func, Func,TResult>) | join … in … on … equals … into … (有关详细信息,请参阅 join 子句。) |
Join(IEnumerable, IEnumerable, Func, Func, Func) | join … in … on … equals … (有关详细信息,请参阅 join 子句。) |
OrderByDescending(IEnumerable, Func) | orderby … descending (有关详细信息,请参阅 orderby 子句。) |
Select | select (有关详细信息,请参阅 let 子句。) |
SelectMany | 多个 from 子句。 (有关详细信息,请参阅 from 子句。) |
ThenBy(IOrderedEnumerable, Func) | orderby …, … (有关详细信息,请参阅 orderby 子句。) |
ThenByDescending(IOrderedEnumerable, Func) | orderby …, … descending (有关详细信息,请参阅 orderby 子句。) |
Where | where (有关详细信息,请参阅 where 子句。) |
OrderBy(IEnumerable, Func) | orderby (有关详细信息,请参阅 orderby 子句。) |
扩展标准查询运算符
通过扩展标准查询的方法,可以增大标准查询运算符的集合。 也可以用自己的实现来替换标准查询运算符,请参考:
其他标准查询运算
通过以下链接可转到一些文章,这些文章基于功能提供有关各种标准查询运算符的附加信息。