Where用于筛选List中满足条件的元素,返回延迟执行的IEnumerable,需调用ToList()等触发执行;注意null安全、性能、FirstOrDefault与SingleOrDefault语义区别、复杂条件提取方法、EF Core中SQL生成与N+1问题。

用 Where 筛选 List 元素最直接
绝大多数时候,你要的只是从 List<t></t> 里挑出满足条件的项,Where 就是为此设计的。它返回 IEnumerable<t></t>,不立即执行,也不修改原集合。
常见错误现象:Where 返回空结果但没报错,容易误以为数据丢了——其实是条件写错了,或者忘了调用 ToList() / ToArray() 触发执行。
- 用法简单:
list.Where(x => x.Status == "Active") - 想立刻拿到新列表?加
.ToList(),否则只是“查询计划” - 注意 null 安全:如果
x.Name可能为 null,别写x.Name.Contains("abc"),改用x.Name?.Contains("abc") == true - 性能上,
Where是 O(n),和 for 循环一样;但链式调用(比如Where+OrderBy)不会多遍历,底层做了优化
FirstOrDefault 和 SingleOrDefault 别混用
要找“第一个匹配项”还是“有且仅有一个匹配项”,语义不同,选错会掩盖逻辑问题。
使用场景:FirstOrDefault 适合查登录用户、默认配置;SingleOrDefault 适合查主键或唯一约束字段(比如根据 ID 查单条记录)。
-
FirstOrDefault找不到时返回default(T)(null 或 0),不会抛异常 -
SingleOrDefault找到多个就直接抛InvalidOperationException:“Sequence contains more than one matching element” - 值类型(如
int)用SingleOrDefault要小心:找不到时也返回 0,可能和真实数据冲突,建议配合bool hasValue判断,或改用可空类型int?
复杂条件别硬塞进一行 lambda
当筛选逻辑涉及多个字段、嵌套判断或外部变量时,强行写成 x => x.A > 0 && x.B != null && x.C.StartsWith("X") 会难读、难调试、难复用。
容易踩的坑:捕获的外部变量在循环中被反复赋值,导致所有 lambda 实际引用同一个值(典型闭包陷阱)。
- 把条件提取成独立方法:
bool IsEligible(MyItem item) { ... },然后list.Where(IsEligible) - 需要动态组合条件?用
Expression<func bool>></func>构建表达式树,尤其对接 Entity Framework 时能翻译成 SQL - 避免在 lambda 中调用非纯函数(比如
DateTime.Now、数据库查询、随机数),副作用不可控
EF Core 里用 LINQ 要防 N+1 查询
在 EF Core 中对导航属性用 Where,比如 context.Orders.Where(o => o.Customer.Name == "Alice"),看起来没问题,但实际可能触发全表扫描或隐式 JOIN,性能骤降。
关键区别:内存中 LINQ(List<t></t>)和 EF Core 的 IQueryable LINQ 行为完全不同——后者会转成 SQL,前者是纯 C# 执行。
- 检查生成的 SQL:启用 EF 日志(
options.LogTo(Console.WriteLine)),确认没出现意外的子查询 - 关联数据要显式加载:用
Include(x => x.Customer),否则o.Customer.Name在内存中访问会触发懒加载(如果开了)或 null 异常 - 分页前务必先
Where再OrderBy最后Skip/Take,顺序反了会导致全表加载到内存再分页
真正麻烦的不是语法,而是搞不清当前 LINQ 是跑在内存里还是数据库里。一个 .AsEnumerable() 调用就能让后续所有操作切到客户端执行,却没有任何警告。


















