【Linq】语言集成查询

发布时间 2023-04-01 07:28:29作者: 小林野夫

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++/CLI尚未支援LINQ。但是有第三方的C++套件[1],以及第三方的PHP套件[2]