C# Entity Framework Core 中的 Include 和 ThenInclude 详解

Entity Framework Core 中的 Include 和 ThenInclude 详解

今天我要分享 Entity Framework Core 中两个非常重要的方法:IncludeThenInclude

1. 什么是导航属性?

在开始之前,我们需要先理解一个核心概念:导航属性

想象一下,在一个图书馆系统中:

  • 一本书属于一个作者
  • 一个作者可以写多本书
  • 一本书可以有多个评论

这种"关联关系"在代码中就用导航属性来表示。

2. 基础数据模型

让我们创建一个简单的博客系统模型:

// 博客类
public class Blog
{
   
   
    public int BlogId {
   
    get; set; }          // 博客ID
    public string Title {
   
    get; set; }        // 博客标题
    public string Url {
   
    get; set; }          // 博客网址
    
    // 导航属性:一个博客有多篇文章
    public List<Post> Posts {
   
    get; set; } = new List<Post>();
}

// 文章类
public class Post
{
   
   
    public int PostId {
   
    get; set; }          // 文章ID
    public string Title {
   
    get; set; }        // 文章标题
    public string Content {
   
    get; set; }      // 文章内容
    
    public int BlogId {
   
    get; set; }          // 外键:属于哪个博客
    
    // 导航属性:文章属于一个博客
    public Blog Blog {
   
    get; set; }
    
    // 导航属性:一篇文章有多个评论
    public List<Comment> Comments {
   
    get; set; } = new List<Comment>();
}

// 评论类
public class Comment
{
   
   
    public int CommentId {
   
    get; set; }       // 评论ID
    public string Text {
   
    get; set; }         // 评论内容
    public string Author {
   
    get; set; }       // 评论作者
    
    public int PostId {
   
    get; set; }          // 外键:属于哪篇文章
    
    // 导航属性:评论属于一篇文章
    public Post Post {
   
    get; set; }
}

3. 问题:为什么需要 Include?

场景分析

如果我们想获取博客及其所有文章,可能会这样写:

// 错误的方式 - 会出现问题!
var blogs = context.Blogs.ToList();

foreach (var blog in blogs)
{
   
   
    Console.WriteLine($"博客: {
     
     blog.Title}");
    
    // 这里会报错或者性能很差!
    foreach (var post in blog.Posts)  // Posts 为 null 或者需要额外查询
    {
   
   
        Console.WriteLine($"  - 文章: {
     
     post.Title}");
    }
}

问题所在:

  • 第一次查询只获取了博客信息
  • 当访问 blog.Posts 时,EF Core 需要再次访问数据库获取文章
  • 这被称为 N+1 查询问题(1次查博客 + N次查文章)

4. Include 的解决方案

Include 的作用:一次性加载关联数据

// 正确的方式 - 使用 Include
var blogs = context.Blogs
    .Include(b => b.Posts)  // 告诉 EF Core:同时加载 Posts 数据
    .ToList();

foreach (var blog in blogs)
{
   
   
    Console.WriteLine($"博客: {
     
     blog.Title}");
    
    // 现在 Posts 已经有数据了!
    foreach (var post in blog.Posts)
    {
   
   
        Console.WriteLine($"  - 文章: {
     
     post.Title}");
    }
}

生成的 SQL 类似:

SELECT b.*, p.* 
FROM Blogs b
LEFT JOIN Posts p ON b.BlogId = p.BlogId

5. ThenInclude:处理多级关联

场景:我们需要博客 → 文章 → 评论

// 只使用 Include 是不够的
var blogs = context.Blogs
    .Include(b => b.Posts)  // 加载到文章
    // 但是文章的评论还是空的!
    .ToList();

// 我们需要 ThenInclude 来继续深入
var detailedBlogs = context.Blogs
    .Include(b => b.Posts)           // 第一层:博客 → 文章
        .ThenInclude(p => p.Comments) // 第二层:文章 → 评论
    .ToList();

可视化理解

博客 (Blog)
  │
  ├── 文章 (Post)          ← Include(b => b.Posts)
  │     │
  │     └── 评论 (Comment)  ← ThenInclude(p => p.Comments)
  │
  └── 其他属性...

6. 实际代码示例

示例 1:基础使用

using (var context = new BlogContext())
{
   
   
    // 加载博客及其文章
    var blogsWithPosts = context.Blogs
        .Include(b => b.Posts)
        .ToList();
    
    // 加载博客、文章及评论
    var blogsWithPostsAndComments = context.Blogs
        .Include(b => b.Posts)           // 第一级:文章
            .ThenInclude(p => p.Comments) // 第二级:评论
        .ToList();
}

示例 2:多层 ThenInclude

// 假设我们还有用户系统
public class User
{
   
   
    public int UserId {
   
    get; set; }
    public string Name {
   
    get; set; }
    public List<Comment> Comments {
   
    get; set; }
}

public class Comment
{
   
   
    // ... 之前的属性
    public int UserId {
   
    get; set; }
    public User User {
   
    get; set; }  // 新增:评论属于用户
}

// 加载博客 → 文章 → 评论 → 用户
var detailedData = context.Blogs
    .Include(b => b.Posts)           // 博客 → 文章
        .ThenInclude(p => p.Comments) // 文章 → 评论
            .ThenInclude(c => c.User) // 评论 → 用户
    .ToList();

示例 3:多个 Include 分支

// 一个博客有作者信息
public class Blog
{
   
   
    // ... 之前的属性
    public int AuthorId {
   
    get; set; }
    public Author Author {
   
    get; set; }  // 博客作者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周杰伦fans

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值