EntityFramework Core 支持多对多关系,即一个实体类型的任意数量的实体与相同或另一个实体类型的任意数量的实体相关联。例如,Post 可以有多个关联的 Tags,而每个 Tag 可以与任意数量的 Posts 关联。
要配置多对多关系,您需要使用一个“联接实体类型”,它映射到关系数据库中的一个“联接表”。这个联接实体类型包含两个外键属性,分别指向关系两端的实体类型。例如,PostTag 类可以包含 PostsId 和 TagsId 属性,分别指向 Post 和 Tag 类的主键属性。
您可以使用 Fluent API 或数据注解来配置多对多关系。Fluent API 提供了更灵活和强大的选项,而数据注解则更简洁和易于阅读。以下是使用 Fluent API 的一个示例https://learn.microsoft.com/zh-cn/ef/core/modeling/relationships/many-to-many1:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagsId),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostsId),
j =>
{
j.HasKey(t => new { t.PostsId, t.TagsId });
});
}
复制
要操作多对多关系,您可以使用导航属性来添加或删除实体之间的关联。导航属性是指从一个实体类型到另一个实体类型的集合或引用属性。例如,Post 类可以有一个 Tags 属性,它是一个 Tag 类型的集合,表示与该 Post 关联的所有 Tag。同样,Tag 类可以有一个 Posts 属性,它是一个 Post 类型的集合,表示与该 Tag 关联的所有 Post。
您可以使用 Add、Remove 或 Attach 方法来修改导航属性中的实体集合。例如,以下代码1将创建一个新的 Post 和两个新的 Tag,并将它们添加到数据库中:
using (var context = new BloggingContext())
{
var post = new Post { Title = "How to configure many-to-many relationships" };
var tag1 = new Tag { Text = "EF Core" };
var tag2 = new Tag { Text = "Many-to-many" };
post.Tags.Add(tag1);
post.Tags.Add(tag2);
context.Add(post);
context.SaveChanges();
}
如果您想要查询多对多关系中的实体,您可以使用 Include 或 ThenInclude 方法来加载导航属性中的相关实体。例如,以下代码1将查询所有的 Post,并加载它们的 Tags:
using (var context = new BloggingContext())
{
var posts = context.Posts.Include(p => p.Tags).ToList();
}
如果您想要通过中间类来操作多对多关系,您需要显式地定义和配置中间类,并在两个一对多关系之间添加导航属性。例如,以下代码2将定义和配置 PostTag 类作为中间类,并在 Post 和 Tag 类上添加 PostTags 属性:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public List<PostTag> PostTags { get; } = new();
}
public class Tag
{
public int Id { get; set; }
public string Text { get; set; }
public List<PostTag> PostTags { get; } = new();
}
public class PostTag
{
public int PostsId { get; set; }
public int TagsId { get; set; }
public Post Post { get; set; }
public Tag Tag { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostsId, t.TagsId });
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostsId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagsId);
}
要通过中间类来操作多对多关系,您可以使用中间类的导航属性来添加或删除实体之间的关联。例如,以下代码2将创建一个新的 Post 和两个新的 Tag,并将它们添加到数据库中:
using (var context = new BloggingContext())
{
var post = new Post { Title = "How to configure many-to-many relationships" };
var tag1 = new Tag { Text = "EF Core" };
var tag2 = new Tag { Text = "Many-to-many" };
post.PostTags.Add(new PostTag { Tag = tag1 });
post.PostTags.Add(new PostTag { Tag = tag2 });
context.Add(post);
context.SaveChanges();
}
如果您想要查询多对多关系中的实体,您可以使用 Include 或 ThenInclude 方法来加载中间类的导航属性中的相关实体。例如,以下代码2将查询所有的 Post,并加载它们的 Tags:
using (var context = new BloggingContext())
{
var posts = context.Posts
.Include(p => p.PostTags)
.ThenInclude(pt => pt.Tag)
.ToList();
}