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

相关推荐
gCode Teacher 格码致知1 小时前
Asp.net Mvc教学: LINQ相关的几大分类的使用率-由Deepseek产生
asp.net·mvc·linq
飞瀑3 小时前
ASP.NET Core MVC 核心架构深度解析
架构·mvc·.net core
无风听海15 小时前
MapStaticAssets()深度解析:ASP.NET Core 静态资源交付的现代范式
后端·asp.net
步步为营DotNet1 天前
探索.NET 11:ASP.NET Core 10 云原生应用开发实践
云原生·asp.net·.net
怪祝浙1 天前
spring boot的启动原理以及mvc和ssm的解释
spring boot·后端·mvc
tongluowan0072 天前
一个请求在Spring MVC 中是怎么流转的
java·spring·mvc
呼Lu噜2 天前
基于C#的ASP.NET Core中分析async、await的使用场景
数据库·c#·asp.net
代码不停2 天前
Spring Web MVC
前端·spring·mvc
无风听海3 天前
深入剖析 YARP 的 Transforms:构建灵活的反向代理转换管道
后端·中间件·asp.net