EF Core 6中多对多关系结合鉴别器的实现问题
问题:EF Core 6中配置带鉴别器的多对多关联(PostgreSQL)
需要在EF Core 6中为Users、Posts、Groups三类实体,通过Tagging实体与Tag建立带鉴别器的多对多关系,数据库用PostgreSQL,目前配置无法正确实现关联。
实体类定义
public class User { public long Id {get; set;} // 其他属性 public ICollection<Tag> Tags {get; set;} }
public class Group{ public long Id {get; set;} // 其他属性 }
public class Post { public long Id {get; set;} // 其他属性 }
public class Tag { public long Id {get; set;} public string Name {get; set;} }
public class Tagging { public long Id {get; set;} public long TagId {get; set;} public long TaggableId {get; set;} public string TaggableType {get; set;} }
已尝试的配置代码
builder.Entity<User>() .HasMany(u => u.Tags) .WithMany(t => t.Users) .UsingEntity<Tagging>( j => j .HasOne(ta => ta.Tag) .WithMany(t => t.Taggings) .HasForeignKey(ta => ta.TagId), j => j .HasOne(ta => ta.User) .WithMany(u => u.Taggings) .HasForeignKey(ta => ta.TaggableId), j => { j.HasKey(ta => ta.Id); } );
核心需求
- TaggableType为
User时,TaggableId关联User的Id - TaggableType为
Post时,TaggableId关联Post的Id - TaggableType为
Group时,TaggableId关联Group的Id
了解过TPT和TPH模式,但配置后仍有问题;尝试过HasQueryFilter,但会全局应用到Post和Group,不符合需求。
10/17更新内容
创建了TaggingGroup子类:
public class TaggingGroup : Tagging { public Group Group { get; set; } }
添加到DbContext并指定基类:
public virtual DbSet<TaggingGroup> TaggingGroups { get; set; } = null!; modelBuilder.Entity<TaggingGroup>(entt => { entt.HasBaseType<Tagging>(); });
配置鉴别器:
entity.HasDiscriminator(ta => ta.TaggableType) .HasValue<TaggingGroup>(TagTaggableTypes.Group) .HasValue<TaggingUser>(TagTaggableTypes.User) .HasValue<TaggingPost>(TagTaggableTypes.Post);
但仍未解决问题,寻求正确实现方案。
正确实现方案
1. 完善Tagging子类及导航属性
为每个可标记实体创建对应的Tagging子类,同时补全主实体和Tag的反向导航:
// User对应的Tagging子类 public class TaggingUser : Tagging { public User User { get; set; } } // Post对应的Tagging子类 public class TaggingPost : Tagging { public Post Post { get; set; } } // Group对应的Tagging子类 public class TaggingGroup : Tagging { public Group Group { get; set; } } // 补全User实体导航 public class User { public long Id {get; set;} // 其他属性 public ICollection<Tag> Tags {get; set;} public ICollection<TaggingUser> Taggings {get; set;} } // 补全Post实体导航 public class Post { public long Id {get; set;} // 其他属性 public ICollection<Tag> Tags {get; set;} public ICollection<TaggingPost> Taggings {get; set;} } // 补全Group实体导航 public class Group{ public long Id {get; set;} // 其他属性 public ICollection<Tag> Tags {get; set;} public ICollection<TaggingGroup> Taggings {get; set;} } // 补全Tag实体的反向导航 public class Tag { public long Id {get; set;} public string Name {get; set;} public ICollection<TaggingUser> UserTaggings {get; set;} public ICollection<TaggingPost> PostTaggings {get; set;} public ICollection<TaggingGroup> GroupTaggings {get; set;} }
2. 配置DbContext与鉴别器
在DbContext中添加所有Tagging子类的DbSet,并配置基类的鉴别器规则:
public class YourDbContext : DbContext { public DbSet<User> Users { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Group> Groups { get; set; } public DbSet<Tag> Tags { get; set; } public DbSet<Tagging> Taggings { get; set; } public DbSet<TaggingUser> TaggingUsers { get; set; } public DbSet<TaggingPost> TaggingPosts { get; set; } public DbSet<TaggingGroup> TaggingGroups { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Tagging基类的鉴别器,指定子类对应的类型值 modelBuilder.Entity<Tagging>() .HasDiscriminator<string>("TaggableType") .HasValue<TaggingUser>("User") .HasValue<TaggingPost>("Post") .HasValue<TaggingGroup>("Group"); // 配置Tagging基类主键 modelBuilder.Entity<Tagging>().HasKey(t => t.Id); } }
3. 配置多对多关联
分别为User、Post、Group配置与Tag的多对多关系,绑定对应的Tagging子类:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 上面的鉴别器配置... // User与Tag的多对多关联 modelBuilder.Entity<User>() .HasMany(u => u.Tags) .WithMany(t => t.UserTaggings) .UsingEntity<TaggingUser>( j => j.HasOne(tu => tu.Tag).WithMany(t => t.UserTaggings).HasForeignKey(tu => tu.TagId), j => j.HasOne(tu => tu.User).WithMany(u => u.Taggings).HasForeignKey(tu => tu.TaggableId), j => j.HasKey(tu => tu.Id) ); // Post与Tag的多对多关联 modelBuilder.Entity<Post>() .HasMany(p => p.Tags) .WithMany(t => t.PostTaggings) .UsingEntity<TaggingPost>( j => j.HasOne(tp => tp.Tag).WithMany(t => t.PostTaggings).HasForeignKey(tp => tp.TagId), j => j.HasOne(tp => tp.Post).WithMany(p => p.Taggings).HasForeignKey(tp => tp.TaggableId), j => j.HasKey(tp => tp.Id) ); // Group与Tag的多对多关联 modelBuilder.Entity<Group>() .HasMany(g => g.Tags) .WithMany(t => t.GroupTaggings) .UsingEntity<TaggingGroup>( j => j.HasOne(tg => tg.Tag).WithMany(t => t.GroupTaggings).HasForeignKey(tg => tg.TagId), j => j.HasOne(tg => tg.Group).WithMany(g => g.Taggings).HasForeignKey(tg => tg.TaggableId), j => j.HasKey(tg => tg.Id) ); }
关键说明
- 采用**TPH(表继承)**模式,所有Tagging子类数据存储在同一个
Taggings表中,通过TaggableType字段区分关联类型 - 每个多对多关系绑定专属的Tagging子类,确保外键关联精准,不会出现跨类型匹配问题
- EF会自动根据子类和鉴别器值处理数据关联,无需额外添加全局查询过滤器
内容的提问来源于stack exchange,提问作者MrDeliK
相关产品推荐
相关产品推荐

