C# Entity Framework Core 中的 Include 和 ThenInclude 详解
Entity Framework Core 中的 Include 和 ThenInclude 详解
-
- 1. 什么是导航属性?
- 2. 基础数据模型
- 3. 问题:为什么需要 Include?
-
- 场景分析
- 4. Include 的解决方案
-
- Include 的作用:一次性加载关联数据
- 5. ThenInclude:处理多级关联
-
- 场景:我们需要博客 → 文章 → 评论
- 可视化理解
- 6. 实际代码示例
-
- 示例 1:基础使用
- 示例 2:多层 ThenInclude
- 示例 3:多个 Include 分支
- 7. 性能考虑
-
- 好的实践:
- 避免的实践:
- 8. 总结
-
- 核心要点:
- 记忆技巧:
- Include 、 ThenInclude
- 核心理解:确实是 `List<Blog>`
- 1. 数据加载状态的对比
-
- 没有使用 Include 的情况:
- 使用 Include 和 ThenInclude 的情况:
- 2. 两层 Include 的用途:解决不同层次的数据需求
-
- 场景 1:只显示博客和文章标题
- 场景 2:显示完整的内容(需要第二层)
- 3. 性能影响:N+1 查询问题
-
- 不好的做法(会产生 N+1 查询):
- 好的做法(1次查询解决所有):
- 4. 实际业务场景举例
-
- 场景 A:博客归档页面(只需要文章列表)
- 场景 B:博客详情页面(需要完整数据)
- 5. 总结
-
- 关键理解:
- C# EF Core联表查询完全指南:从入门到精通
-
- 🎯 准备工作:建立数据模型
- 📊 联表查询类型对比
- 🔍 1. INNER JOIN(内连接)
-
- 概念解释
- LINQ 实现方式
- SQL 语句实现
- 🔍 2. LEFT JOIN(左外连接)
-
- 概念解释
- LINQ 实现方式
- SQL 语句实现
- 🔍 3. RIGHT JOIN(右外连接)
-
- 概念解释
- LINQ 实现方式
- SQL 语句实现
- 🔍 4. FULL JOIN(全外连接)
-
- 概念解释
- LINQ 实现方式
- SQL 语句实现
- 🚀 实际应用示例
- 💡 最佳实践与性能建议
今天我要分享 Entity Framework Core 中两个非常重要的方法:Include 和 ThenInclude。
1. 什么是导航属性?
在开始之前,我们需要先理解一个核心概念:导航属性。
想象一下,在一个图书馆系统中:
- 一本书属于一个作者
- 一个作者可以写多本书
- 一本书可以有多个评论
这种"关联关系"在代码中就用导航属性来表示。
2. 基础数据模型
让我们创建一个简单的博客系统模型:
// 博客类
public class Blog
{public int BlogId { get; set; } // 博客IDpublic 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; } // 文章IDpublic 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; } // 评论IDpublic 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; } // 博客作者
