语言集成查询(Language-Integrated Query),简称LINQ,.NET中的LINQ体系如下图所示:
在编程语言层次,LINQ对于不同的数据源提供了相同的查询语法,方便了程序员操作不同的数据源。
可查询类型
LINQ之所以能够使用相同的语法操作不同的数据源,是因为和LINQ直接打交道的是可查询类型而非数据源,在LINQ中,直接或间接实现了IEnumerable<T>
接口的类型称为可查询类型, .NET中如:List<T>
,Dictionary<TKey,TValue>
,数组(由CLR负责隐式实现IEnumerable<T>接口)等,实现了IEnumerable<T>接口。
IQueryable<out T>继承自IEnumerable<T>,是个标记接口。
可查询类型无需额外操作即可进行LINQ操作,若数据源在内存中不以可查询类型的形式存在,那么LINQ提供程序必须要先将数据源转换为可查询类型,如LINQ to XML
将XML文件转换为可查询的XElement
类型:
XElement contacts = XElement.Load(@"c:myContactList.xml");
LINQ 提供程序
LINQ提供程序(LINQ Provider)提供了对特定的数据源进行标准的LINQ操作及一些扩展操作(如:LINQ to XML),不同的LINQ提供程序对于一些相同名称的扩展方法会提供不同的实现方式。.NET中预定义的LINQ提供程序包括:LINQ to Object、LINQ to XML (C#)、LINQ to SQL、LINQ to DataSet、LINQ to Entities。LINQ to SQL
不建议使用,用LINQ to Entities
来替代。
LINQ查询包含三个步骤:
- 获取数据源
- 创建查询语句
- 执行查询
LINQ查询方式
-
LINQ 表达式(又称为查询表达式)
以from
关键字开头,select
关键字结尾。 -
扩展方法(又称为标准查询)
System.Linq.Enumerable
类和System.Linq.Queryable
类,分别针对IEnumerable<T>
和IQueryable<T>
接口进行的扩展。.NET也提供了几个对IEnumerable和IQueryable接口进行操作的扩展方法,如: Cast<TResult>和OfType<TResult>。 -
LINQ 表达式和扩展方法混合使用
(from e in Employees where e.Salary>8000 select e).ToList()
LINQ表达式和扩展方法对比:
LINQ表达式和扩展方法在编译后的代码没有什么区别
- 对于排序、分组、联合查询使用LINQ表达式更为方便
//以排序为例,使用年龄、姓名、邮箱进行排序, //LINQ表达式中使用逗号分隔排序字段,而扩展方法则需要多次调用相应的扩展方法 var result= from e in Employees where e.Age>50 && e.Salary>8000 orderby e.Age,e.Name,e.Email select e; //等价的扩展方法 var result=Employees .Where(e=>e.Age>50 && e.Salary>8000) .OrderBy(e=>e.Age) .ThenBy(e=>e.Name) .ThenBy(e=>e.Email);
- 扩展方法能够提供比LINQ表达式更复杂的查询
//取第26行到36行范围内的数据 var result=Employees.Skip(25).Take(10); //使用LINQ表达式我表示写不出来......
LINQ表达式是对常用扩展方法在语法层面上的简化,LINQ表达式有着更好的可读性,在编译时LINQ表达式会被转化为对扩展方法的调用。
LINQ查询特点:
-
延迟查询
若查询表达式的返回结果是IEnumerable<T>
类型,则在声明查询表达式时不会执行查询,而是在迭代查询变量时才进行查询。贴一幅MSDN上的经典LINQ查询流程图(延迟查询):
-
立即查询
若查询表达式返回单个值或者使用了ToList<T>
、ToArray<T>
等方法时会执行立即查询,因为这些操作会遍历数据。
一句话总结,若查询表达式不包含对数据源的遍历操作则执行延迟查询,否则会进行立即查询。
LINQ表达式中的查询关键字
表格中的英文没什么难点,就不翻译了 :)
关键字 | 描述 |
---|---|
from | Specifies a data source and a range variable (similar to an iteration variable). |
where | Filters source elements based on one or more Boolean expressions separated by logical AND and OR operators. |
select | Specifies the type and shape that the elements in the returned sequence will have when the query is executed. |
group | Groups query results according to a specified key value. |
into | Provides an identifier that can serve as a reference to the results of a join, group or select clause. |
orderby | Sorts query results in ascending or descending order based on the default comparer for the element type. |
join | Joins two data sources based on an equality comparison between two specified matching criteria. |
let | Introduces a range variable to store sub-expression results in a query expression. |
in | Contextual keyword in a join clause. |
on | Contextual keyword in a join clause. |
equals | Contextual keyword in a join clause. |
by | Contextual keyword in a group clause. |
ascending | Contextual keyword in an orderby clause. |
descending | Contextual keyword in an orderby clause. |
两个接口
在LINQ中,一个查询表达式被编译为表达式树或者委托,查询结果为IEnumerable<T>
类型则被编译为委托,查询结果是IQueryable
或IQueryable<T>
类型则被编译为表达式树,在运行时表达式树会被解析为适合于数据源的查询语句。
- System.Collection.Generic.IEnumerable<T>
IEnumerable
先将数据放到本地内存中,然后再执行过滤操作(如果有的话),适合于对当前进程中的数据进行查询操作,如:LINQ to XML
、LINQ to Object
。
- System.Linq.IQueryable<T>
在执行查询操作时,IQueryable
先在服务器端进行过滤操作(如果有的话),然后再将数据放到本地内存中。IQueryable
适合使用对进程外(如数据库)的数据进行查询操作,如:LINQ to Entities
。
两个命名空间
System.Linq
System.Linq命名空间中包含用于LINQ查询的类和接口
System.Linq.Expressions
System.Linq.Expressions 命名空间包含了用于创建表达式树的类、 接口。
LINQ的优缺点
优点
- 对不同的数据源提供了几乎一致的查询操作,这可使我们更多的去关注业务逻辑而非对数据源的操作
- 提供编译期的类型检查
- 在书写LINQ查询表达式时可以使用Visual Studio的智能提示
- 调试方便
缺点
- 对于复杂的查询操作显得力不从心
- 容易写出性能不高的查询表达式
结语
本篇是自己学习LINQ的总结,不求面面俱到。通篇以文字叙述为主,辅以少量代码,若有错误希望大家指出。
工具推荐
LINQ Pad是一款轻量级的数据查询工具,在LINQ Pad中可以使用LINQ表达式、扩展方法、SQL语句等对数据库进行操作,简单易用功能强大。
书目推荐:
《LINQ Interview Questions Answers》
参考文章
Introduction to LINQ Queries (C#)
Standard Query Operators Overview (C#)
Query Expression Syntax for Standard Query Operators (C#)
Data Transformations with LINQ (C#)
LINQ provider basics
Enabling a Data Source for LINQ Querying
LINQ: Building an IQueryable Provider – Part I