EF Core同实体多对多关联(Products与RelatedProducts)问题求助
我想给Product实体添加RelatedProducts关联,和Category的关联类型一样,但目标是同一实体(Product)。常规多对多关联能正常工作,所以示例里已经移除了。
目标:
实现产品与产品之间的自引用多对多关联,通过中间表RelatedProducts维护关联关系,每个产品可以关联多个其他产品。
异常信息:
"Exception occured: Cannot create a relationship between 'Product.RelatedProducts' and 'RelatedProduct.Related' because a relationship already exists between 'Product.RelatedProducts' and 'RelatedProduct.Product'. Navigation properties can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'RelatedProduct.Related' first in 'OnModelCreating'."
现有代码
Product.cs
public class Product : IExportable, IEntityBase { public int Id { get; set; } public string Name { get; set;} public ICollection<RelatedProduct> RelatedProducts { get; set; } }
ProductEntityTypeConfiguration.cs
internal class ProductEntityTypeConfiguration : IEntityTypeConfiguration<Product> { public void Configure(EntityTypeBuilder<Product> builder) { builder.HasKey(p => p.Id); } }
RelatedProduct.cs
public class RelatedProduct { public int Id { get; set; } public int ProductId { get; set; } public virtual Product Product { get; set; } public int RelatedId { get; set; } public virtual Product Related { get; set; } }
RelatedProductEntityTypeConfiguration.cs
public class RelatedProductsEntityConfiguration : IEntityTypeConfiguration<RelatedProduct> { public void Configure(EntityTypeBuilder<RelatedProduct> builder) { builder.HasKey(rp => rp.Id); builder.ToTable("RelatedProducts"); builder .HasOne(rp => rp.Product) .WithMany(p => p.RelatedProducts) .HasForeignKey(rp => rp.ProductId) .OnDelete(DeleteBehavior.Restrict); builder .HasOne(rp => rp.Related) .WithMany(p => p.RelatedProducts) .HasForeignKey(rp => rp.RelatedId) .OnDelete(DeleteBehavior.Restrict); } }
问题原因
报错的核心是:Product.RelatedProducts这个导航属性被同时绑定到了两个关联关系(对应中间表的Product和Related两个外键),但EF Core不允许一个导航属性参与多个关联关系。
解决方法
给Product实体添加两个独立的导航属性,分别对应中间表的两个外键关联,再分别配置映射关系:
1. 更新Product实体类
添加一个新的导航属性,用来表示当前产品作为“被关联产品”时的关联记录:
public class Product : IExportable, IEntityBase { public int Id { get; set; } public string Name { get; set;} // 当前产品作为主产品,关联的其他产品记录 public ICollection<RelatedProduct> RelatedProducts { get; set; } // 当前产品作为关联产品,被其他产品关联的记录 public ICollection<RelatedProduct> ProductOfRelated { get; set; } }
2. 修改中间表的配置类
分别将两个外键关联到对应的导航属性:
public class RelatedProductsEntityConfiguration : IEntityTypeConfiguration<RelatedProduct> { public void Configure(EntityTypeBuilder<RelatedProduct> builder) { builder.HasKey(rp => rp.Id); builder.ToTable("RelatedProducts"); // 配置主产品到关联记录的关系 builder .HasOne(rp => rp.Product) .WithMany(p => p.RelatedProducts) .HasForeignKey(rp => rp.ProductId) .OnDelete(DeleteBehavior.Restrict); // 配置关联产品到被关联记录的关系 builder .HasOne(rp => rp.Related) .WithMany(p => p.ProductOfRelated) .HasForeignKey(rp => rp.RelatedId) .OnDelete(DeleteBehavior.Restrict); } }
可选优化:使用复合主键避免重复关联
如果不需要中间表的自增ID,可以把ProductId和RelatedId设为复合主键,防止同一个产品关联对被重复添加:
// 修改RelatedProduct类 public class RelatedProduct { public int ProductId { get; set; } public virtual Product Product { get; set; } public int RelatedId { get; set; } public virtual Product Related { get; set; } } // 修改配置类的主键设置 public void Configure(EntityTypeBuilder<RelatedProduct> builder) { builder.HasKey(rp => new { rp.ProductId, rp.RelatedId }); // 复合主键 builder.ToTable("RelatedProducts"); // 其余关联配置不变... }
内容的提问来源于stack exchange,提问作者Tobias Lie-Atjam

