LINQ(语言集成查询)
官方文档:Language Integrated Query (LINQ) – C# | Microsoft Learn LINQ全称:Language‑Integrated Query,中文:语言集成查询。 .NET版本:C#3.0 / .NET 3.5 开始内置,无需额外安装。需要命名空间
using System.Linq;,新版.NET支持隐式全局using,老版本需要手动添加。
传统方式查询数据需要写字符串形式SQL,编译不做类型检查、没有智能提示;不同数据源(数据库、XML、数组)要学不同查询语法。LINQ把查询变成C#语言一等语法构造,数组、List、XML、数据库可以使用一套查询写法。
LINQ主要分支
- LINQ to Objects:操作内存集合(数组、List、IEnumerable)
- LINQ to XML:操作XML文档(需要引用
System.Xml.Linq) - LINQ to DataSet:操作DataSet数据集
- LINQ to SQL:操作SQL Server数据库(老技术)
- LINQ to Entity(EF / EF Core):ORM框架,把LINQ翻译为SQL访问数据库
LINQ中把可迭代的对象集合叫做序列(sequence),绝大多数序列实现
IEnumerable<T>接口;针对数据库远程查询则是IQueryable<T>,会生成表达式树,延迟翻译成SQL执行。
延迟执行(重要):定义LINQ查询表达式的时候不会执行查询;只有遍历(
foreach)、调用FirstOrDefault()、Count()这类方法时才真正执行查询。
示例1:数组查询(LINQ to Objects)
需求:字符串数组,筛选以.cn结尾的域名
using System;
using System.Linq;
class Program
{
static void Main(string[] args)
{
string[] siteNames = { "www.foxdevelop.cn", "www.foxdevelop.com", "www.foxdevelop.net", "www.baidu.cn" };
// LINQ查询语法:定义查询,此时不执行
var realSite =
from s in siteNames // from:遍历数据源,每个元素放入变量s
where s.EndsWith(".cn") // where:过滤条件
select s; // select:输出结果
// FirstOrDefault() 获取第一条,没有返回null;First()没有数据直接抛异常
Console.WriteLine(realSite.FirstOrDefault());
// LastOrDefault() 获取最后一条匹配
Console.WriteLine(realSite.LastOrDefault());
// foreach触发查询执行,遍历全部结果
foreach (var item in realSite)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
Code language: JavaScript (javascript)
输出:
www.foxdevelop.com
www.baidu.cn
www.foxdevelop.com
www.baidu.cn
Code language: CSS (css)
语法理解(类比SQL)
from s in siteNames // FROM siteNames 把每一个元素给到s变量
where s.EndsWith(".cn") // WHERE 条件过滤
select s; // SELECT 返回结果
Code language: JavaScript (javascript)
提示:
First():取第一条,无数据抛异常FirstOrDefault():取第一条,无数据返回默认值(引用类型返回null)Last()/LastOrDefault()同理取最后一条
LINQ有两套写法:查询语法(from‑where‑select) 和方法语法(链式调用Where()/Select()),编译器会把查询语法编译成标准查询方法调用,两者语义、性能完全等价。
等价方法语法写法:
var realSite = siteNames.Where(s => s.EndsWith(".cn"));
Code language: JavaScript (javascript)
示例2:LINQ to XML 查询XML文档
需要引入程序集:
System.Xml.Linq,命名空间:using System.Xml.Linq;
using System;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleDemo
{
class Program
{
static void Main(string[] args)
{
// 构造XML字符串解析为XElement对象
XElement books = XElement.Parse(@"
<books>
<book>
<title>C#入门</title>
<price>12.34</price>
</book>
<book>
<title>Python教程</title>
<price>3.5</price>
</book>
</books>");
// 查询:取出价格小于10元的书籍标题
var titles =
from book in books.Elements("book")
where (double)book.Element("price") < 10.0
select book.Element("title");
foreach (var item in titles)
{
Console.WriteLine(item.Value);
}
Console.ReadKey();
}
}
}
Code language: HTML, XML (xml)
输出:
Python教程
关键API
XElement.Parse():将XML文本解析成内存XML对象.Elements("book"):获取子节点.Element("price"):获取第一个匹配子节点.Value:读取节点文本值
本地查询 vs 远程数据库查询(IEnumerable 与 IQueryable)
IEnumerable<T>(LINQ to Objects):内存集合,查询编译为委托,所有过滤、逻辑全部在本地内存执行。数组、List返回的序列属于这个。IQueryable<T>(EF Core、LINQ To SQL):针对远程数据源,编译器编译为表达式树;EF Core解析表达式树翻译成SQL语句交给数据库执行,减少内存开销。
简单IQueryable provider只支持少量查询;EF Core属于复杂Provider,可以把完整LINQ翻译成SQL,同时做类型映射。
LINQ可以做的事
- 筛选
where - 排序
orderby - 投影
select(转换输出字段、类型) - 分组
group by - 聚合:
Count()、Max()、Sum()(这类没有查询子句,只能调用方法语法) - 集合转换、类型转换
LINQ不只是查询,也可以做数据转换,例如字符串数组转数字集合,排序输出。
总结
- 查询变量只是保存查询逻辑,延迟执行,多次foreach会重复执行查询。
First()/Last()在无数据时报错;优先用xxxOrDefault()更安全。- LINQ to XML需要手动引用
System.Xml.Linq。 - 查询语法和链式方法语法可以混合使用,没有性能差异。
- 操作数据库尽量使用
IQueryable<T>,让过滤逻辑交给数据库执行,不要把全部数据拉到内存再过滤。