You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core中Route与City复杂关系配置报错:主键重复

解决EF Core中Route与City复杂关系的主键重复问题

问题根源分析

你遇到的主键重复错误,核心原因有两个:

  1. 多对多中间实体RouteFromCity/RouteToCity未显式配置复合主键,EF Core无法识别RouteId+CityId的唯一约束;
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 16:54:51