C# LINQ查询方法 C#如何使用LINQ查询数据

来源:这里教程网 时间:2026-02-21 17:41:19 作者:

LINQ 查询不是“用不用”的问题,而是“怎么用对、用稳、用得明白”的问题。它本质是一套统一的查询语法和方法集,核心在于理解

IEnumerable<t></t>
IQueryable<t></t>
的区别,以及何时该用方法语法、何时该用查询表达式。

什么时候该用
Where
而不是
First
Single

这是最常混淆的起点:三者都做筛选,但语义和异常行为完全不同。

Where
返回所有匹配项(
IEnumerable<t></t>
),安全、可链式调用,适合后续继续处理
First
返回第一个匹配项,没找到抛
InvalidOperationException
;加
OrDefault
版本(
FirstOrDefault
)则返回
default(T)
(如
null
0
Single
要求**有且仅有一个**匹配项,多于一个或零个都抛异常;
SingleOrDefault
仅在零个时返回默认值,多个仍报错

典型误用:用

Single
查用户列表中邮箱匹配的记录——一旦数据库有脏数据(重复邮箱),线上直接崩溃。应优先用
FirstOrDefault
,再显式判断是否为
null

IQueryable<t></t>
IEnumerable<t></t>
混用导致 N+1 查询

EF Core / NHibernate 等 ORM 中,

IQueryable<t></t>
是“可翻译的表达式树”,而
IEnumerable<t></t>
是“内存集合”。一旦调用
ToList()
ToArray()
或任何强制执行的方法,查询就落地了。

错误写法:
var users = context.Users.ToList(); // 已加载全部用户到内存
var activeOrders = users.Where(u => u.IsActive).SelectMany(u => u.Orders).ToList(); // N+1:每用户查一次 Orders
正确写法:
var activeOrders = context.Users
    .Where(u => u.IsActive)
    .SelectMany(u => u.Orders)
    .ToList(); // 整个表达式由 EF 转成一条 SQL

关键判断点:看变量声明类型。如果变量是

IQueryable<t></t>
,且没被中间调用过
AsEnumerable()
或强制执行方法,那它还在“可翻译”状态。

查询表达式 vs 方法语法:选哪个?

两者编译后完全等价,选择只取决于可读性和场景。

多表 join、group by、let 临时变量时,查询表达式更贴近 SQL 思维,例如:
var query = from u in context.Users
              join o in context.Orders on u.Id equals o.UserId into userOrders
              from o in userOrders.DefaultIfEmpty()
              select new { u.Name, OrderCount = userOrders.Count() };
简单链式过滤、转换、聚合(
Count
Average
Any
)用方法语法更直接,例如:
bool hasPremiumUser = context.Users.Any(u => u.Tier == "Premium");
混合使用常见且合法:先用查询表达式做复杂结构,再用方法语法收尾,如
query.OrderBy(x => x.Name).Take(10)

注意:

let
在方法语法中对应的是
Select
+ 匿名类型或元组,但可读性会下降,不推荐强行转。

延迟执行陷阱:
Count()
Count
属性的区别

IEnumerable<t></t>
没有
Count
属性,只有
Count()
方法;而
IQueryable<t></t>
Count()
会被翻译成
SELECT COUNT(*)
,但
Count
属性(如
List<t>.Count</t>
)是 O(1) 内存访问。

错误:对
IQueryable<t></t>
反复调用
Count()
—— 每次都发一次 COUNT 查询
正确:需要多次用数量时,先
var count = query.Count();
缓存结果;若还需数据,再
var list = query.ToList();
特别注意:不要写
if (query.Count() > 0)
,改用
query.Any()
,后者生成
EXISTS
,性能高得多

真正容易被忽略的,是把

IQueryable<t></t>
传给多个方法后,在每个方法里都无意识触发了执行——比如日志、验证、分页计算,全都各自发了一次 SQL。

相关推荐