Entity Framework Core中多对多关系实现及相关技术问题咨询
多对多关系场景下的方案及问题解答
参考的Stack Overflow评论提到:最简单的方法是为关联表添加主键(PK)。另外要注意,使用“鉴别器(discriminator)”方案会产生“非规范化模型(denormalized model)”,这在数据库领域并不理想。比如如果“收藏(Fav)”关系有专属属性(如评分列表示对剧集的喜爱程度),“观看列表(Watchlist)”关系有专属属性(如计划观看日期),那么不同类型的行会存在大量空值:收藏行的日期列为空,观看列表行的评分列为空。不过该方案因单表查询简洁,仍常被选用。
我的场景包含Book和Person实体类,代码如下:
public class Book { [Key] public int ID { get; set; } [Required] public string Title { get; set; } public string? Subtitle { get; set; } public ICollection<PersonBook> Authors { get; set; } public ICollection<PersonBook> Reviewers { get; set; } public ICollection<PersonBook> Translators { get; set; } } public class Person { [Key] public int ID { get; set; } [Required] public string Name { get; set; } public ICollection<PersonBook> BooksAuthored { get; set; } public ICollection<PersonBook> BooksReviewed { get; set; } public ICollection<PersonBook> BooksTranslated { get; set; } } public class PersonBook { [ForeignKey("Book")] public int BookID { get; set; } public Book Book { get; set; } [ForeignKey("Person")] public int PersonID { get; set; } public Person Person { get; set; } }
若采用为PersonBook添加鉴别器/附加属性的方案,我有以下问题:
- 向数据库添加实体时,如何设置该键?
- 如何解决非规范化模型的问题?
- 除了提到的两种方案外,是否存在第三种解决方案?
问题解答
1. 鉴别器属性的设置方法
首先在PersonBook类中添加鉴别器字段,比如:
public string RelationshipType { get; set; }
然后在EF Core的模型配置中指定鉴别器规则:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<PersonBook>() .HasDiscriminator<string>("RelationshipType") .HasValue("Author") .HasValue("Reviewer") .HasValue("Translator"); }
添加实体时,直接给RelationshipType赋值即可:
var authorRelation = new PersonBook { BookID = bookId, PersonID = personId, RelationshipType = "Author" }; context.PersonBooks.Add(authorRelation); context.SaveChanges();
2. 非规范化模型的优化方案
非规范化模型的空值问题无法完全消除,但可以通过以下方式降低影响:
- 使用可空类型:给关系专属属性标记为可空(比如
public int? Rating { get; set; }、public DateTime? PlannedDate { get; set; }),避免强制非空导致的插入错误。 - 业务层校验:添加实体时,根据
RelationshipType确保对应属性有值,比如添加Reviewer关系时必须填写Rating,添加Translator时无需维护该字段。 - 数据库约束:通过数据库条件约束,确保特定类型的行必须填写对应属性,比如给
RelationshipType = 'Reviewer'的行添加Rating IS NOT NULL的约束。
3. 第三种解决方案:拆分独立关联表
针对不同关系类型创建单独的关联表,比如BookAuthor、BookReviewer、BookTranslator,每个表只维护对应关系的专属属性:
public class BookAuthor { public int BookID { get; set; } public Book Book { get; set; } public int PersonID { get; set; } public Person Person { get; set; } // 作者专属属性,比如签约日期 public DateTime ContractDate { get; set; } } public class BookReviewer { public int BookID { get; set; } public Book Book { get; set; } public int PersonID { get; set; } public Person Person { get; set; } // 评论者专属属性,比如评分 public int Rating { get; set; } }
这种方案彻底避免了非规范化问题,每个表的属性都是必要字段,不会出现空值。缺点是需要维护多个关联表,查询时需分别关联不同表。
内容的提问来源于stack exchange,提问作者noobie
相关产品推荐
相关产品推荐

