找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1544

积分

0

好友

200

主题
发表于 2026-2-12 17:57:25 | 查看: 32| 回复: 0

C# LINQ左连接教学卡通插画

在处理数据关联查询时,你是否经常遇到这样的场景:需要获取主表(如所有用户)的全部记录,同时关联出从表(如订单)的匹配信息,对于没有匹配项的记录,也需要展示出来?这就是典型的“左连接(Left Join)”操作。在C#的LINQ查询中,如何高效地实现它呢?

一、LINQ Left Join 的核心思路

LINQ本身并未直接提供名为“LeftJoin”的方法。实现左连接的秘诀在于组合使用 join ... into ... (查询语法)或 GroupJoin (方法语法)与关键的 DefaultIfEmpty() 方法。

简单来说,DefaultIfEmpty() 方法的作用是:当右表(从表)没有匹配数据时,它会返回一个包含默认值(对于引用类型是 null)的序列。正是这一步,确保了左表(主表)的所有记录都能被保留,从而实现了左连接的效果。

二、完整示例:查询所有用户及其订单

我们先来定义一个简单的业务场景,使用“用户”和“订单”两个类,并通过LINQ查询语法(推荐新手使用,更贴近SQL思维)来实现左连接。

using System;
using System.Collections.Generic;
using System.Linq;

// 定义用户类(左表)
public class User
{
    public int UserId { get; set; }
    public string UserName { get; set; }
}

// 定义订单类(右表)
public class Order
{
    public int OrderId { get; set; }
    public int UserId { get; set; } // 关联用户ID
    public string OrderName { get; set; }
}

class Program
{
    static void Main()
    {
        // 模拟数据源
        List<User> users = new List<User>()
        {
            new User { UserId = 1, UserName = "张三" },
            new User { UserId = 2, UserName = "李四" },
            new User { UserId = 3, UserName = "王五" } // 王五没有订单
        };

        List<Order> orders = new List<Order>()
        {
            new Order { OrderId = 101, UserId = 1, OrderName = "手机" },
            new Order { OrderId = 102, UserId = 1, OrderName = "电脑" },
            new Order { OrderId = 103, UserId = 2, OrderName = "书籍" }
        };

        // LINQ 左连接查询(查询语法)
        var leftJoinResult = from u in users
                             join o in orders on u.UserId equals o.UserId into orderGroup
                             // DefaultIfEmpty() 确保无匹配订单时返回null,实现左连接
                             from og in orderGroup.DefaultIfEmpty()
                             select new
                             {
                                 用户名 = u.UserName,
                                 用户ID = u.UserId,
                                 订单ID = og?.OrderId ?? 0, // 空合并运算符处理null
                                 订单名称 = og?.OrderName ?? "无订单"
                             };

        // 输出结果
        foreach (var item in leftJoinResult)
        {
            Console.WriteLine($"用户名:{item.用户名},用户ID:{item.用户ID},订单ID:{item.订单ID},订单名称:{item.订单名称}");
        }
    }
}

三、代码关键点解析

  1. join o in orders on u.UserId equals o.UserId into orderGroup
    这一行执行了一个“分组连接”。它将用户表和订单表按照 UserId 进行关联,并将每个用户匹配到的所有订单作为一个分组(IEnumerable<Order>)放入 orderGroup 变量中。

  2. from og in orderGroup.DefaultIfEmpty()
    这是实现左连接的灵魂。它从上一步的分组结果中进行遍历。DefaultIfEmpty() 方法保证了即使某个用户的 orderGroup 为空(即该用户没有任何订单),本次遍历也会产生一个元素,其值为 null(因为 Order 是引用类型)。这使得用户“王五”的记录得以保留。

  3. og?.OrderId ?? 0
    由于 og 可能为 null,直接访问其属性会引发 NullReferenceException。这里使用了空条件运算符 ?. 进行安全访问,并结合空合并运算符 ??null 情况提供一个友好的默认值(如0或“无订单”)。

四、方法语法实现 Left Join

如果你更习惯于链式调用的方法语法,可以使用 GroupJoin 配合 SelectMany 来实现完全相同的功能:

var leftJoinResult = users
    .GroupJoin(orders, // 右表
               u => u.UserId, // 左表关联键选择器
               o => o.UserId, // 右表关联键选择器
               (u, orderGroup) => new { u, orderGroup }) // 结果选择器,形成分组
    .SelectMany( // 展开分组(相当于查询语法的第二个from)
        ug => ug.orderGroup.DefaultIfEmpty(), // 对每个分组应用空值处理
        (ug, og) => new // 最终投影
        {
            用户名 = ug.u.UserName,
            用户ID = ug.u.UserId,
            订单ID = og?.OrderId ?? 0,
            订单名称 = og?.OrderName ?? "无订单"
        });

五、程序运行输出

执行上述任意一种写法的代码,你都将得到以下结果,清晰展示了左连接的效果:

用户名:张三,用户ID:1,订单ID:101,订单名称:手机
用户名:张三,用户ID:1,订单ID:102,订单名称:电脑
用户名:李四,用户ID:2,订单ID:103,订单名称:书籍
用户名:王五,用户ID:3,订单ID:0,订单名称:无订单

总结与要点回顾

  1. 核心组合:LINQ左连接 = join ... into ... (或 GroupJoin) + DefaultIfEmpty()。两者缺一不可。
  2. 语法选择查询语法from ... join ...)在表达连接操作时结构更清晰,更接近SQL,建议初学者优先掌握。方法语法GroupJoin)则更符合函数式编程风格,灵活且强大。
  3. 空值处理:务必警惕右表字段可能为 null 的情况,善用 ?.?? 运算符来避免运行时异常并提升数据可读性。



上一篇:深度解析:天工Skywork如何基于Google Gemini 3打造Windows桌面级AI Agent
下一篇:电容器的工作原理与选型指南:从18世纪莱顿瓶到现代电路设计
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 12:58 , Processed in 0.674173 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表