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 分的男生,只返回姓名和成绩。

csharp 复制代码
public class Student
{
    public string Name { get; set; }
    public int Score { get; set; }
    public string Gender { get; set; }
}

void Main()
{
    List<Student> students = new List<Student>
    {
        new Student { Name = "张三", Score = 85, Gender = "男" },
        new Student { Name = "李四", Score = 92, Gender = "男" },
        new Student { Name = "王芳", Score = 78, Gender = "女" },
        new Student { Name = "赵磊", Score = 88, Gender = "男" }
    };

    var result = students
        .Where(s => s.Score > 80 && s.Gender == "男")
        .Select(s => new { s.Name, s.Score });

    foreach (var item in result)
        Console.WriteLine($"{item.Name} - {item.Score}");
    // 输出:张三 - 85, 李四 - 92, 赵磊 - 88
}

案例 2:排序(OrderBy / ThenBy)

场景:按成绩降序排列,成绩相同则按姓名升序。

csharp 复制代码
var sorted = students
    .OrderByDescending(s => s.Score)
    .ThenBy(s => s.Name);

foreach (var s in sorted)
    Console.WriteLine($"{s.Name}: {s.Score}");

案例 3:分组(GroupBy)

场景:按性别分组,统计每组人数和平均分。

csharp 复制代码
var groups = students.GroupBy(s => s.Gender)
    .Select(g => new 
    {
        Gender = g.Key,
        Count = g.Count(),
        AvgScore = g.Average(x => x.Score)
    });

foreach (var g in groups)
    Console.WriteLine($"{g.Gender} 共 {g.Count} 人,平均分 {g.AvgScore:F1}");

案例 4:连接(Join)

场景:两个集合(学生表和班级表),通过班级 ID 连接,显示学生及其班级名称。

csharp 复制代码
public class StudentWithClassId
{
    public string Name { get; set; }
    public int ClassId { get; set; }
}

public class Class
{
    public int Id { get; set; }
    public string Name { get; set; }
}

void Main()
{
    List<StudentWithClassId> students = new List<StudentWithClassId>
    {
        new StudentWithClassId { Name = "张三", ClassId = 1 },
        new StudentWithClassId { Name = "李四", ClassId = 2 },
    };

    List<Class> classes = new List<Class>
    {
        new Class { Id = 1, Name = "一班" },
        new Class { Id = 2, Name = "二班" }
    };

    var joined = students.Join(classes,
        student => student.ClassId,
        cls => cls.Id,
        (student, cls) => new { student.Name, ClassName = cls.Name });

    foreach (var item in joined)
        Console.WriteLine($"{item.Name} 属于 {item.ClassName}");
}

二、LINQ to Entities(4 个案例)

假设使用 Entity Framework Core ,上下文为 SchoolDbContext,包含 DbSet<Student>DbSet<Class>。查询会被翻译成 SQL 执行。

案例 1:筛选与投影

场景:查询数据库中成绩大于 80 分的男生,只返回姓名和成绩。

csharp 复制代码
using (var context = new SchoolDbContext())
{
    var result = context.Students
        .Where(s => s.Score > 80 && s.Gender == "男")
        .Select(s => new { s.Name, s.Score })
        .ToList();   // 此时执行 SQL

    foreach (var item in result)
        Console.WriteLine($"{item.Name} - {item.Score}");
}

案例 2:排序 + 取前 N 条

场景:按成绩降序取前 3 名的学生。

csharp 复制代码
using (var context = new SchoolDbContext())
{
    var top3 = context.Students
        .OrderByDescending(s => s.Score)
        .Take(3)
        .ToList();

    foreach (var s in top3)
        Console.WriteLine($"{s.Name}: {s.Score}");
}

案例 3:分组聚合(GroupBy)

场景:按性别分组,统计每组人数和最高分。

csharp 复制代码
using (var context = new SchoolDbContext())
{
    var groupStats = context.Students
        .GroupBy(s => s.Gender)
        .Select(g => new
        {
            Gender = g.Key,
            Count = g.Count(),
            MaxScore = g.Max(x => x.Score)
        })
        .ToList();

    foreach (var g in groupStats)
        Console.WriteLine($"{g.Gender} 共 {g.Count} 人,最高分 {g.MaxScore}");
}

注意:EF Core 会将上述 GroupBy 转换为 SQL 的 GROUP BY,不会将数据全部拉到内存。

案例 4:跨表连接(Join / Include)

场景:查询学生及其所属班级名称。两种写法:

方法 A:使用 Join(类似 SQL)
csharp 复制代码
using (var context = new SchoolDbContext())
{
    var query = context.Students
        .Join(context.Classes,
            s => s.ClassId,
            c => c.Id,
            (s, c) => new { s.Name, ClassName = c.Name })
        .ToList();
}
方法 B:使用导航属性(推荐,如果模型已配置关系)
csharp 复制代码
// 假设 Student 实体包含 public Class Class { get; set; }
var result = 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 ObjectsLINQ to Entities 各 4 个新案例,避免与上一组重复。


一、LINQ to Objects(追加 4 例)

案例 5:聚合函数(Min, Max, Sum, Average)

场景:对一组订单金额进行计算,获取总额、最高金额、最低金额、平均金额。

csharp 复制代码
List<double> orderAmounts = new List<double> { 99.5, 150.0, 320.8, 45.2, 270.3 };

double total = orderAmounts.Sum();
double max = orderAmounts.Max();
double min = orderAmounts.Min();
double avg = orderAmounts.Average();

Console.WriteLine($"总额:{total},最高:{max},最低:{min},平均:{avg:F2}");

案例 6:集合操作(Distinct, Union, Intersect, Except)

场景:两个兴趣集合,求并集、交集、差集、去重。

csharp 复制代码
List<string> groupA = new List<string> { "篮球", "足球", "游泳" };
List<string> groupB = new List<string> { "足球", "羽毛球", "游泳" };

var union = groupA.Union(groupB);       // 并集:篮球,足球,游泳,羽毛球
var intersect = groupA.Intersect(groupB); // 交集:足球,游泳
var except = groupA.Except(groupB);       // 差集:篮球
var distinct = groupA.Distinct();         // 本身去重(本例无变化)

Console.WriteLine($"并集:{string.Join(",", union)}");
Console.WriteLine($"交集:{string.Join(",", intersect)}");
Console.WriteLine($"差集:{string.Join(",", except)}");

案例 7:元素操作(First, Last, Single 及 OrDefault 版本)

场景:从集合中获取特定位置的元素,安全处理空值。

csharp 复制代码
List<int> numbers = new List<int> { 10, 20, 30, 40, 50 };
List<int> emptyList = new List<int>();

int first = numbers.First();                     // 10
int last = numbers.Last();                       // 50
int firstEven = numbers.First(n => n % 20 == 0); // 20

// 安全版本(不存在时返回默认值,不抛异常)
int firstOrDefault = emptyList.FirstOrDefault(); // 0
int singleOrDefault = numbers.SingleOrDefault(n => n > 100); // 0

// Single:要求集合中恰好有一个匹配元素
int onlyOne = new List<int> { 100 }.Single();    // 100

案例 8:量词操作(Any, All, Contains)

场景:判断集合中是否存在满足条件的元素,或是否全部满足。

csharp 复制代码
List<int> ages = new List<int> { 18, 20, 25, 17, 30 };

bool hasMinor = ages.Any(age => age < 18);      // true(有17岁)
bool allAdult = ages.All(age => age >= 18);     // false(存在17)
bool contains25 = ages.Contains(25);            // true

if (hasMinor)
    Console.WriteLine("存在未成年人");

二、LINQ to Entities(追加 4 例)

仍假设使用 EF Core,上下文为 AppDbContext,实体如 Product(含 Name, Price, Category, CreatedAt)、Order 等。

案例 5:模糊查询与日期范围筛选

场景:查询名称包含"手机"的产品,且生产日期在去年全年范围内。

csharp 复制代码
using (var context = new AppDbContext())
{
    var startDate = new DateTime(2025, 1, 1);
    var endDate = new DateTime(2025, 12, 31);

    var products = 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 页的数据。

csharp 复制代码
int pageNumber = 3;
int pageSize = 10;

using (var context = new AppDbContext())
{
    var page = context.Products
        .OrderBy(p => p.Price)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .ToList();
}

案例 7:子查询与 Exists(用 Any 实现)

场景:查询所有下过订单的客户(即客户在 Orders 表中存在关联记录)。

csharp 复制代码
using (var context = new AppDbContext())
{
    // 方法1:直接使用 Any 子查询(会被翻译为 EXISTS)
    var customersWithOrders = context.Customers
        .Where(c => context.Orders.Any(o => o.CustomerId == c.Id))
        .ToList();

    // 方法2:Join 后 Distinct(效率通常不如 Any)
    var customersWithOrders2 = context.Customers
        .Join(context.Orders, c => c.Id, o => o.CustomerId, (c, o) => c)
        .Distinct()
        .ToList();
}

案例 8:多级分组(GroupBy 多个字段)

场景:按商品分类和上架年份分组,统计每组的商品数量和平均价格。

csharp 复制代码
using (var context = new AppDbContext())
{
    var query = 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 (var item in query)
        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) 扫描 依赖数据库索引和查询优化

如果需要更多案例(如 SelectManyZipToDictionary 等),我可以继续提供。

相关推荐
她说彩礼65万1 天前
Asp.net core 返回类型
数据库·后端·asp.net
她说彩礼65万2 天前
Asp.net core返回类型总结
后端·asp.net
她说彩礼65万2 天前
Asp.net core ContenResult
后端·asp.net
步步为营DotNet3 天前
借助 Microsoft.Extensions.AI 与 ASP.NET Core 10 实现智能 Web 应用故障预测
人工智能·microsoft·asp.net
品尚公益团队4 天前
C#在asp.net网页开发中的带cookie登录实现
前端·c#·asp.net
wang09074 天前
自己动手写一个spring之MVC_3
java·spring·mvc
shepherd1264 天前
一次把 Spring MVC 文件上传参数“查没了”的排查:multipart、Filter 与 request body 的连环坑
java·后端·spring·mvc
阿pin4 天前
Android随笔-MVC/MVP/MVVM/MVI
android·mvc·mvvm·mvp·mvi
time展天15 天前
Dddify:给 ASP.NET Core 项目一套轻量、清晰、可落地的 DDD 基础设施
数据库·后端·asp.net
拉长的苗条5 天前
细说 ASP.NET Cache 及其高级用法
后端·asp.net