Asp.net Mvc教学:LINQ to Objects和 LINQ to Entities的经典案例-由Deepseek产生
下面分别给出LINQ to Objects(操作内存集合)和LINQ to Entities(通过 EF Core 操作数据库)的 4 个典型案例。案例使用 C# 编写,并附带简要说明。
一、LINQ to Objects(4 个案例)
适用于List<T>,T[],IEnumerable<T>等内存中的集合。
案例 1:筛选(Where)与投影(Select)
场景:从学生列表中找出成绩大于 80 分的男生,只返回姓名和成绩。
publicclassStudent{publicstringName{get;set;}publicintScore{get;set;}publicstringGender{get;set;}}voidMain(){List<Student>students=newList<Student>{newStudent{Name="张三",Score=85,Gender="男"},newStudent{Name="李四",Score=92,Gender="男"},newStudent{Name="王芳",Score=78,Gender="女"},newStudent{Name="赵磊",Score=88,Gender="男"}};varresult=students.Where(s=>s.Score>80&&s.Gender=="男").Select(s=>new{s.Name,s.Score});foreach(variteminresult)Console.WriteLine($"{item.Name}-{item.Score}");// 输出:张三 - 85, 李四 - 92, 赵磊 - 88}案例 2:排序(OrderBy / ThenBy)
场景:按成绩降序排列,成绩相同则按姓名升序。
varsorted=students.OrderByDescending(s=>s.Score).ThenBy(s=>s.Name);foreach(varsinsorted)Console.WriteLine($"{s.Name}:{s.Score}");案例 3:分组(GroupBy)
场景:按性别分组,统计每组人数和平均分。
vargroups=students.GroupBy(s=>s.Gender).Select(g=>new{Gender=g.Key,Count=g.Count(),AvgScore=g.Average(x=>x.Score)});foreach(vargingroups)Console.WriteLine($"{g.Gender}共{g.Count}人,平均分{g.AvgScore:F1}");案例 4:连接(Join)
场景:两个集合(学生表和班级表),通过班级 ID 连接,显示学生及其班级名称。
publicclassStudentWithClassId{publicstringName{get;set;}publicintClassId{get;set;}}publicclassClass{publicintId{get;set;}publicstringName{get;set;}}voidMain(){List<StudentWithClassId>students=newList<StudentWithClassId>{newStudentWithClassId{Name="张三",ClassId=1},newStudentWithClassId{Name="李四",ClassId=2},};List<Class>classes=newList<Class>{newClass{Id=1,Name="一班"},newClass{Id=2,Name="二班"}};varjoined=students.Join(classes,student=>student.ClassId,cls=>cls.Id,(student,cls)=>new{student.Name,ClassName=cls.Name});foreach(variteminjoined)Console.WriteLine($"{item.Name}属于{item.ClassName}");}二、LINQ to Entities(4 个案例)
假设使用Entity Framework Core,上下文为SchoolDbContext,包含DbSet<Student>和DbSet<Class>。查询会被翻译成 SQL 执行。
案例 1:筛选与投影
场景:查询数据库中成绩大于 80 分的男生,只返回姓名和成绩。
using(varcontext=newSchoolDbContext()){varresult=context.Students.Where(s=>s.Score>80&&s.Gender=="男").Select(s=>new{s.Name,s.Score}).ToList();// 此时执行 SQLforeach(variteminresult)Console.WriteLine($"{item.Name}-{item.Score}");}案例 2:排序 + 取前 N 条
场景:按成绩降序取前 3 名的学生。
using(varcontext=newSchoolDbContext()){vartop3=context.Students.OrderByDescending(s=>s.Score).Take(3).ToList();foreach(varsintop3)Console.WriteLine($"{s.Name}:{s.Score}");}案例 3:分组聚合(GroupBy)
场景:按性别分组,统计每组人数和最高分。
using(varcontext=newSchoolDbContext()){vargroupStats=context.Students.GroupBy(s=>s.Gender).Select(g=>new{Gender=g.Key,Count=g.Count(),MaxScore=g.Max(x=>x.Score)}).ToList();foreach(vargingroupStats)Console.WriteLine($"{g.Gender}共{g.Count}人,最高分{g.MaxScore}");}注意:EF Core 会将上述 GroupBy 转换为 SQL 的
GROUP BY,不会将数据全部拉到内存。
案例 4:跨表连接(Join / Include)
场景:查询学生及其所属班级名称。两种写法:
方法 A:使用 Join(类似 SQL)
using(varcontext=newSchoolDbContext()){varquery=context.Students.Join(context.Classes,s=>s.ClassId,c=>c.Id,(s,c)=>new{s.Name,ClassName=c.Name}).ToList();}方法 B:使用导航属性(推荐,如果模型已配置关系)
// 假设 Student 实体包含 public Class Class { get; set; }varresult=context.Students.Include(s=>s.Class)// 避免 N+1 问题.Select(s=>new{s.Name,ClassName=s.Class.Name}).ToList();关键区别总结
| 特性 | LINQ to Objects | LINQ to Entities |
|---|---|---|
| 数据源 | 内存集合(List, Array等) | 数据库(通过 EF Core) |
| 执行方式 | 立即执行,委托直接调用 | 延迟执行,生成 SQL 语句 |
| 支持的方法 | 几乎所有 LINQ 运算符 | 受 SQL 限制(例如不能随意调用自定义 C# 方法) |
| 典型终止操作 | .ToList(),.First(),.Count()等 | 同样需要.ToList()等来触发 SQL 执行 |
掌握这两者,基本就能覆盖绝大多数 .NET 日常开发中的数据查询需求。
继续补充LINQ to Objects和LINQ to Entities各 4 个新案例,避免与上一组重复。
一、LINQ to Objects(追加 4 例)
案例 5:聚合函数(Min, Max, Sum, Average)
场景:对一组订单金额进行计算,获取总额、最高金额、最低金额、平均金额。
List<double>orderAmounts=newList<double>{99.5,150.0,320.8,45.2,270.3};doubletotal=orderAmounts.Sum();doublemax=orderAmounts.Max();doublemin=orderAmounts.Min();doubleavg=orderAmounts.Average();Console.WriteLine($"总额:{total},最高:{max},最低:{min},平均:{avg:F2}");案例 6:集合操作(Distinct, Union, Intersect, Except)
场景:两个兴趣集合,求并集、交集、差集、去重。
List<string>groupA=newList<string>{"篮球","足球","游泳"};List<string>groupB=newList<string>{"足球","羽毛球","游泳"};varunion=groupA.Union(groupB);// 并集:篮球,足球,游泳,羽毛球varintersect=groupA.Intersect(groupB);// 交集:足球,游泳varexcept=groupA.Except(groupB);// 差集:篮球vardistinct=groupA.Distinct();// 本身去重(本例无变化)Console.WriteLine($"并集:{string.Join(",",union)}");Console.WriteLine($"交集:{string.Join(",",intersect)}");Console.WriteLine($"差集:{string.Join(",",except)}");案例 7:元素操作(First, Last, Single 及 OrDefault 版本)
场景:从集合中获取特定位置的元素,安全处理空值。
List<int>numbers=newList<int>{10,20,30,40,50};List<int>emptyList=newList<int>();intfirst=numbers.First();// 10intlast=numbers.Last();// 50intfirstEven=numbers.First(n=>n%20==0);// 20// 安全版本(不存在时返回默认值,不抛异常)intfirstOrDefault=emptyList.FirstOrDefault();// 0intsingleOrDefault=numbers.SingleOrDefault(n=>n>100);// 0// Single:要求集合中恰好有一个匹配元素intonlyOne=newList<int>{100}.Single();// 100案例 8:量词操作(Any, All, Contains)
场景:判断集合中是否存在满足条件的元素,或是否全部满足。
List<int>ages=newList<int>{18,20,25,17,30};boolhasMinor=ages.Any(age=>age<18);// true(有17岁)boolallAdult=ages.All(age=>age>=18);// false(存在17)boolcontains25=ages.Contains(25);// trueif(hasMinor)Console.WriteLine("存在未成年人");二、LINQ to Entities(追加 4 例)
仍假设使用 EF Core,上下文为AppDbContext,实体如Product(含Name, Price, Category, CreatedAt)、Order等。
案例 5:模糊查询与日期范围筛选
场景:查询名称包含“手机”的产品,且生产日期在去年全年范围内。
using(varcontext=newAppDbContext()){varstartDate=newDateTime(2025,1,1);varendDate=newDateTime(2025,12,31);varproducts=context.Products.Where(p=>EF.Functions.Like(p.Name,"%手机%")&&p.CreatedAt>=startDate&&p.CreatedAt<=endDate).ToList();}
EF.Functions.Like会被翻译成 SQL 的LIKE,比Contains更灵活。
案例 6:分页(Skip + Take)
场景:每页显示 10 条商品,按价格升序,获取第 3 页的数据。
intpageNumber=3;intpageSize=10;using(varcontext=newAppDbContext()){varpage=context.Products.OrderBy(p=>p.Price).Skip((pageNumber-1)*pageSize).Take(pageSize).ToList();}案例 7:子查询与 Exists(用 Any 实现)
场景:查询所有下过订单的客户(即客户在 Orders 表中存在关联记录)。
using(varcontext=newAppDbContext()){// 方法1:直接使用 Any 子查询(会被翻译为 EXISTS)varcustomersWithOrders=context.Customers.Where(c=>context.Orders.Any(o=>o.CustomerId==c.Id)).ToList();// 方法2:Join 后 Distinct(效率通常不如 Any)varcustomersWithOrders2=context.Customers.Join(context.Orders,c=>c.Id,o=>o.CustomerId,(c,o)=>c).Distinct().ToList();}案例 8:多级分组(GroupBy 多个字段)
场景:按商品分类和上架年份分组,统计每组的商品数量和平均价格。
using(varcontext=newAppDbContext()){varquery=context.Products.GroupBy(p=>new{p.Category,Year=p.CreatedAt.Year}).Select(g=>new{g.Key.Category,g.Key.Year,Count=g.Count(),AvgPrice=g.Average(p=>p.Price)}).OrderBy(r=>r.Category).ThenBy(r=>r.Year).ToList();foreach(variteminquery)Console.WriteLine($"{item.Category}-{item.Year}:{item.Count}件,均价{item.AvgPrice:F2}");}EF Core 会将
CreatedAt.Year翻译为 SQL 的YEAR(CreatedAt),支持常见日期函数。
再次总结两组案例的核心区别
| 操作类型 | LINQ to Objects | LINQ to Entities |
|---|---|---|
| 自定义方法调用 | ✅ 完全支持(如自定义比较器) | ❌ 仅支持 EF Core 映射的函数 |
| 延迟执行 | 内存中遍历时执行 | 生成 SQL,ToList()时执行 |
| 异常处理 | 直接抛出 .NET 异常 | 可能抛出数据库相关异常 |
| 性能考虑 | 数据量过大时 O(n) 扫描 | 依赖数据库索引和查询优化 |
如果需要更多案例(如SelectMany、Zip、ToDictionary等),我可以继续提供。
