【Linq】语言集成查询
前言
从应用程序的角度来看,原始源数据的特定类型和结构并不重要。 应用程序始终将源数据视为 IEnumerable<T> 或 IQueryable<T> 集合。数据库中一行就是一个可以枚举序列。
ints.Where(p => p % 2 == 0).ToArray(); p就是一个序列,p可以式数据库中的行,也可以式数组中item,也可式一个对象。
LINQ查询时有两种语法可供选择:
查询表达式语法(Query Expression):使用查询运算符;
流利语法(Fluent Syntax):利用System.Linq.Enumerable类中定义的扩展方法和Lambda表达式方式进行查询。
CLR本身并不理解查询表达式语法,它只理解流利语法。编译器负责把查询表达式语法编译为流利语法。
流利语法
int[] ints={1,2,3,4}; var result = ints.Where(p => p % 2 == 0).ToArray();
对比流利语法和C#的传统语法:
// extension methods make LINQ elegant IEnumerable<string> query = names .Where(n => n.Contains("a")) .OrderBy(n => n.Length) .Select(n => n.ToUpper()); // static methods lose query's fluency IEnumerable<string> query2 = Enumerable.Select( Enumerable.OrderBy( Enumerable.Where(names, n => n.Contains("a") ), n => n.Length ), n => n.ToUpper() );
标准的查询运算符(Standard query operators)
System.Linq.Enumerable静态类声明了一套标准查询操作符(Standard Query Operators,SQO)方法集合。基本语法如下:
using (var db = new EntityContext()) { var roles = from o in db.Users where o.Account == "Apollo" select o.Roles; … }
标准查询操作符和Lambda表达式的关系非常密切。编译器会将上述表达式转化为下述以Lambda表达式为参数的显式扩展方法调用序列:
using (var db = new EntityContext()) { var roles = db.Users.Where(o => o.Account == "Apollo").Select(o => o.Roles); }
LINQ的各式言語支援度
操作符 | 类别 | 语义 | 流利语法示例 | 查询表达式语法示例 |
---|---|---|---|---|
Where | 筛选操作符(Restriction) | Predicate→bool |
var user =
db.Users.Where(
o => o.Roles != null);
|
var users =
from o in db.Users
where o.Roles != null
select o;
|
Select | 投影操作符(Projection) | 将对象投影为一个匿名类型实例 TSource→TResult |
var users =
db.Users.Select
(o => new { o.Account, o.Password });
|
var users =
from o in db.Users
select new { o.Account, o.Password };
|
SelectMany | 投影操作符(Projection) | 返回多行结果,用于多表的交叉连接(cross join) |
Dim res = Employees.SelectMany(Function(e)
e.Family.Select(Function(c)c.name))
|
|
Skip | 分块操作符(Partitioning) | 跳过前n个元素 |
var users =
db.Users.OrderBy(
o => o.Roles.Count
).Skip(10);
|
|
SkipWhile | 分块操作符(Partitioning) | 跳过起始处使条件为真的所有元素 |
var users =
db.Users.OrderBy(
o => o.Roles.Count).SkipWhile(
o => o.Roles == 3);
|
|
Take | 分块操作符(Partitioning) | 返回开头之处的n个元素 |
var users =
db.Users.OrderBy(
o => o.Roles.Count).Take(5);
|
|
TakeWhile | 分块操作符(Partitioning) | 返回起始处使条件为真的所有元素 |
var users =
db.Users.OrderBy(
o => o.Roles.Count).TakeWhile(
o => o.Roles.Count == 3);
|
|
Join | 连接操作符 | 内连接两个或多个表,仅限于Equals运算符 |
var categoriesProducts =
from c in nWEntities.Categories
join p in nWEntities.Products
on c.CategoryID equals p.CategoryID
into productsByCategoryID
select new
{
c.CategoryName,
productCount =
productsByCategoryID.Count()
};
|
|
GroupJoin | 连接操作符 | 类似于LEFT OUTER JOIN,右侧集合匹配于左侧集合键值的元素被分组 |
From cust In customers Group
Join ord In orders
On cust.CustomerID
Equals ord.CustomerID
Into CustomerOrders = Group,
OrderTotal = Sum(ord.Total)
|
|
Concat | 合并操作符 | 用于连接两个序列 | returnValue = firstSeq.Concat(secondSeq) | |
OrderBy | 排序操作符(Ordering) | 升序排列 TSource→TKey |
var users =
db.Users.OrderBy(
o => o.Roles.Count);
|
var users =
from o in db.Users
orderby o.Roles.Count
select o;
|
OrderByDescending | 排序操作符(Ordering) | 降序排列 |
var users =
db.Users.OrderByDescending(
o => o.Roles.Count);
|
var users =
from o in db.Users
orderby o.Roles.Count descending
select o;
|
ThenBy | 排序操作符(Ordering) | 只能对IOrderedEnumerable接口对象使用 | ||
ThenByDescending | 排序操作符(Ordering) | 只能对IOrderedEnumerable接口对象使用 | ||
Reverse | 排序操作符(Ordering) | 只能对IOrderedEnumerable接口对象使用 | ||
GroupBy | 分组操作符 |
var users =
db.Users.GroupBy(
o => o.Roles.Count);
|
var users =
from o in db.Users
group o by o.Roles.Count into g
select new { RoleCount = g.Key, Group = g };
|
|
Distinct | 集合操作符 | 去重复 |
var roles =
user.Roles.Distinct();
|
|
Union | 集合操作符 | 集合并,还去重复 |
var roles =
user1.Roles.Union(
user2.Roles);
|
|
Intersect | 集合操作符 | 集合交 |
var roles =
user1.Roles.Intersect(
user2.Roles);
|
|
Except | 集合操作符 | 集合差 |
var roles =
user1.Roles.Except(
user2.Roles);
|
|
AsEnumerable | 转换操作符 | 用于把一个IEnumerable的派生类型转化为IEnumerable类型 | ||
AsQueryable | 转换操作符 | IEnumerable(Of T)转化为IQueryable(Of T). | ||
ToArray | 转换操作符 | 转换为数组 | ||
ToList | 转换操作符 | 转换为List | ||
ToDictionary | 转换操作符 | 转换为一对一的字典(键-值对的集合) | ||
ToLookup | 转换操作符 | 转换为一对多的字典(键-值集的集合) | ||
OfType | 转换操作符 | 获取指定类型的元素组成一个数组 |
object[] numbers = { null, 1.0,
"two", 3, "four", 5,
"six", 7.0 };
var doubles =
numbers.OfType<double>();
|
|
Cast | 转换操作符 | 把序列的所有元素转换为指定类型 | ||
SequenceEqual | 相等操作符 | 两个序列的元素依次相同返回真。 使用元素所属类的IEqualityComparer(Of T) 泛型界面做相等比较 |
||
First | 元素操作符 | 返回序列第一个元素(或满足条件第一个元素),没有则异常 | ||
FirstOrDefault | 元素操作符 | 返回序列第一个元素,没有则返回空或默认值 |
var user =
db.Users.FirstOrDefault(
o => o.Roles.Count == 3);
|
|
Last | 元素操作符 | 返回序列最后一个元素,没有则异常 | ||
LastOrDefault | 元素操作符 | 返回序列最后一个元素,没有则返回空或默认值 |
var user =
db.Users.LastOrDefault(
o => o.Roles.Count == 3);
|
|
Single | 元素操作符 | 返回序列唯一元素,如果没有元素或多个元素则异常 | ||
SingleOrDefault | 元素操作符 | 返回序列唯一元素,如果多个元素则异常 |
var user = db.Users.SingleOrDefault(o => o.Account == "Apollo");
|
|
ElementAt | 元素操作符 | 返回序列指定元素,失败则异常 | ||
ElementAtOrDefault | 元素操作符 | 返回序列指定元素,失败则空或默认值 | ||
DefaultIfEmpty | 元素操作符 | 返回序列,如果序列为空则返回元素的默认值 |
For Each number As Integer
In numbers.DefaultIfEmpty()
output.AppendLine(number)
Next
|
|
All | 量词操作符 | 序列所有元素满足条件则为真 |
var result
= db.Users.All(
o => o.Roles.Count == 3);
|
|
Any | 量词操作符 | 序列有一个元素满足条件则为真 |
var result
= db.Users.Any(
o => o.Roles.Count == 3);
|
|
Contains | 量词操作符 | 是否包含一个元素 |
var result =
db.Users.Where(
o => o.Roles.Count == 3
).Contains(user1);
|
|
Count | 聚合统计操作符 | 计数,可选一个谓词 |
var result
= db.Users.Count
(o => o.Roles.Count == 3);
|
|
LongCount | 聚合统计操作符 | 计数,返回Int64类型 | ||
Sum | 聚合统计操作符 | 求和,可选对一个lambda函数表达式 |
var result
= db.Users.Sum
(o => o.Roles.Count);
|
|
Min | 聚合统计操作符 | 最小值,可选对一个lambda函数表达式 |
var result
= db.Users.Min
(o => o.Roles.Count);
|
|
Max | 聚合统计操作符 |
var result
= db.Users.Max
(o => o.Roles.Count);
|
||
Average | 聚合统计操作符 |
var result
= db.Users.Average
(o => o.Roles.Count);
|
||
Aggregate | 聚合统计操作符 | 参数为一个委托,在序列的每个元素上执行该委托。 委托的第一个参数为当前累计值,第二个参数为当前元素, 返回值为新的累计值 |
Dim reversed As String =
words.Aggregate(
Function(ByVal current,
ByVal word)
word & " " & current)
|
|
equals/Equals | 关键字 | 用于Join子句 | ||
from/From | 关键字 | |||
in/In | 关键字 | 指出数据源 | ||
into/Into | 关键字 | 用于Group By子句 | ||
key | 关键字 | 用于Group By子句的无名类型 | ||
let | 关键字 | 给表达式定义别名 |
From prod In products
Let Discount =
prod.UnitPrice * 0.1
Where Discount >= 50
Select prod.ProductName,
prod.UnitPrice, Discount
|
|
Group | 关键字 | 在GroupBy子句的Into中用于辨识分组结果 |
From num In numbers
Group num By
remainder5 =
(num Mod 5) Into Group
|
|
Range | 方法 | 产生一个整数序列 |
From n In
Enumerable.Range(100, 50)
|
|
Repeat | 方法 | 产生一个整数序列 |
From n In
Enumerable.Repeat(7, 10)
|
LINQ的各式言語支援度
下列的言語支持LINQ。
- C# 3.0
- F# 1.1.8.1
- Visual Basic 2008(9.0)
编程是个人爱好