EF Core中Route与City复杂关系配置报错:主键重复
解决EF Core中Route与City复杂关系的主键重复问题
问题根源分析
你遇到的主键重复错误,核心原因有两个:
- 多对多中间实体
RouteFromCity/RouteToCity未显式配置复合主键,EF Core无法识别RouteId+CityId的唯一约束; City类中未区分不同关系的反向导航,导致EF Core自动推断关系时混淆了一对多(DeparturePlace/ArrivePlace)与多对多(FromCities/ToCities)的映射逻辑,进而触发重复插入。
分步解决方案
1. 修正中间实体,添加复合主键
给RouteFromCity和RouteToCity明确配置RouteId+CityId的复合主键,确保每个关联组合唯一:
public class RouteFromCity { // 复合主键:RouteId + CityId public int RouteId { get; set; } public int CityId { get; set; } public Route Route { get; set; } = null!; public City City { get; set; } = null!; } public class RouteToCity { // 复合主键:RouteId + CityId public int RouteId { get; set; } public int CityId { get; set; } public Route Route { get; set; } = null!; public City City { get; set; } = null!; }
2. 扩展City类,添加区分关系的反向导航
在City中新增对应不同关系的反向导航属性,避免EF混淆映射:
public class City { [Key] public int Id { get; set; } [Column(TypeName = "nvarchar(35)")] public string Name { get; set; } = null!; public byte CountryId { get; set; } [DeleteBehavior(DeleteBehavior.NoAction)] public Country Country { get; set; } = null!; [DeleteBehavior(DeleteBehavior.NoAction)] public ICollection<Branch>? Branches { get; set; } // 一对多反向导航:作为出发地的Route集合 public ICollection<Route>? DepartureRoutes { get; set; } // 一对多反向导航:作为目的地的Route集合 public ICollection<Route>? ArriveRoutes { get; set; } // 多对多反向导航:属于Route的FromCities集合 public ICollection<Route>? FromRoutes { get; set; } // 多对多反向导航:属于Route的ToCities集合 public ICollection<Route>? ToRoutes { get; set; } }
3. 修正Fluent API关系配置
在OnModelCreating中明确每个关系的映射逻辑,包括主键、外键和导航关联:
// 配置Route与DeparturePlace的一对多关系 modelBuilder.Entity<Route>() .HasOne(r => r.DeparturePlace) .WithMany(c => c.DepartureRoutes) .HasForeignKey(r => r.DeparturePlaceId) .OnDelete(DeleteBehavior.NoAction); // 配置Route与ArrivePlace的一对多关系 modelBuilder.Entity<Route>() .HasOne(r => r.ArrivePlace) .WithMany(c => c.ArriveRoutes) .HasForeignKey(r => r.ArrivePlaceId) .OnDelete(DeleteBehavior.NoAction); // 配置Route与FromCities的多对多关系 modelBuilder.Entity<Route>() .HasMany(r => r.FromCities) .WithMany(c => c.FromRoutes) .UsingEntity<RouteFromCity>( j => j.HasOne<City>(rc => rc.City).WithMany(c => c.FromRoutes).HasForeignKey(rc => rc.CityId), j => j.HasOne<Route>(rc => rc.Route).WithMany(r => r.FromCities).HasForeignKey(rc => rc.RouteId), j => j.HasKey(rc => new { rc.RouteId, rc.CityId }) // 显式指定复合主键 ); // 配置Route与ToCities的多对多关系 modelBuilder.Entity<Route>() .HasMany(r => r.ToCities) .WithMany(c => c.ToRoutes) .UsingEntity<RouteToCity>( j => j.HasOne<City>(rc => rc.City).WithMany(c => c.ToRoutes).HasForeignKey(rc => rc.CityId), j => j.HasOne<Route>(rc => rc.Route).WithMany(r => r.ToCities).HasForeignKey(rc => rc.RouteId), j => j.HasKey(rc => new { rc.RouteId, rc.CityId }) // 显式指定复合主键 );
4. 检查业务代码,避免重复添加
在操作FromCities/ToCities集合时,确保不会重复添加同一个City:
// 示例:安全添加City到FromCities var route = dbContext.Routes.Include(r => r.FromCities).First(r => r.Id == 1013); var targetCity = dbContext.Cities.First(c => c.Id == 2); if (!route.FromCities.Any(c => c.Id == targetCity.Id)) { route.FromCities.Add(targetCity); } dbContext.SaveChanges();
内容的提问来源于stack exchange,提问作者Mahmoud Shahrour
相关产品推荐
相关产品推荐

