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 等),我可以继续提供。

相关推荐
IKUN家族6 小时前
Spring MVC(四)
java·spring·mvc
玖石书13 小时前
ASP.NET Core迁移Spring系列:运行时与 GC 篇
后端·spring·asp.net
玖石书1 天前
ASP.NET Core 迁移至 Spring系列:类库框架篇
java·后端·asp.net
Volunteer Technology1 天前
Kafka的消费全流程
分布式·kafka·linq
玖石书1 天前
ASP.NET Core 迁移至 Spring系列:编译生态篇
后端·spring·asp.net
玖石书2 天前
ASP.NET Core 迁移至 Spring系列:语言基础篇
spring·asp.net·技术栈迁移
「KISSSHOT」4 天前
抽象与性能:从 LINQ 看现代 .NET 的优化之道
java·.net·linq
香菜TTT4 天前
Kafka_深度解析_从架构原理到生产实践
分布式·架构·kafka·linq
明如正午5 天前
【C#】LINQ_HashSet_BitField解析
c#·linq
米码收割机5 天前
【C#】ASP.NET Web 车辆信息管理系统(源码+文档)【独一无二】
前端·c#·asp.net