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

Entity Framework Core 8多对多关系数据更新主键重复问题

问题

在使用Code First迁移构建Entity Framework Core Web项目时,执行数据更新方法时出现以下错误:

Violation of PRIMARY KEY constraint 'PK_ApplicationUserLanguages'. Cannot insert duplicate key in object 'dbo.ApplicationUserLanguages'. The duplicate key value is (29, a7314beb-fcbc-4e28-b331-6e2f5d3c44e8).

数据结构定义:

public class Languages
{
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public int Id { get; set; }
     public string Name { get; set; }
     public Guid? UserId { get; set; }
     [JsonIgnore]
     public virtual List<ApplicationUser>? Users { get; set; }
}

public class ApplicationUser : IdentityUser
{
    public List<Languages>? Languages { get; set; }
}

DbContext代码:

public DbSet<Languages> Languages { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Languages>().HasMany(l => l.Users).WithMany(u => u.Languages);

modelBuilder.Entity<Languages>().HasData(
   new Languages { Id = 1, Name = "English" },
   new Languages { Id = 2, Name = "Spanish" },
   new Languages { Id = 3, Name = "French" },
   new Languages { Id = 4, Name = "German" },
   new Languages { Id = 5, Name = "Italian" },
   new Languages { Id = 6, Name = "Portuguese" },
   new Languages { Id = 7, Name = "Dutch" },
   new Languages { Id = 8, Name = "Russian" },
   new Languages { Id = 9, Name = "Chinese" },
   new Languages { Id = 10, Name = "Japanese" },
   new Languages { Id = 11, Name = "Korean" },
   new Languages { Id = 12, Name = "Arabic" },
   new Languages { Id = 13, Name = "Hindi" },
   new Languages { Id = 14, Name = "Bengali" },
   new Languages { Id = 15, Name = "Urdu" },
   new Languages { Id = 16, Name = "Punjabi" },
   new Languages { Id = 17, Name = "Telugu" },
   new Languages { Id = 18, Name = "Marathi" },
   new Languages { Id = 19, Name = "Tamil" },
   new Languages { Id = 20, Name = "Gujarati" },
   new Languages { Id = 21, Name = "Kannada" },
   new Languages { Id = 22, Name = "Odia" },
   new Languages { Id = 23, Name = "Malayalam" },
   new Languages { Id = 24, Name = "Sindhi" },
   new Languages { Id = 25, Name = "Assamese" },
   new Languages { Id = 26, Name = "Nepali" },
   new Languages { Id = 27, Name = "Sanskrit" },
   new Languages { Id = 28, Name = "Sindhi" });
}

数据更新代码:

var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == updateProfile.Id);

if (user != null)
{
    user.Languages = updateProfile.Languages;

    _context.Entry(user).State = EntityState.Modified;
    await _context.SaveChangesAsync();
}

尝试先删除现有语言关联再重新添加,仍会出现该错误,期望EF Core能自动管理外键,但不清楚问题所在。

解决方案

问题根源

  1. 实体关系冲突:同时定义了两种关系——Languages中的UserId字段暗示一对多关系(一个用户对应多个语言),但又通过HasMany.WithMany配置了多对多关系。EF Core优先使用Fluent API配置,但UserId字段会生成额外外键,最终引发冲突。
  2. 更新方式错误:直接替换user.Languages集合时,EF Core会将传入的updateProfile.Languages实体视为未被追踪的新实体,尝试插入重复的关联记录,导致主键冲突。

修复步骤

1. 清理实体定义,明确多对多关系

删除Languages类中的UserId字段,多对多关系不需要该字段,EF Core会自动生成中间表:

public class Languages
{
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public int Id { get; set; }
     public string Name { get; set; }
     [JsonIgnore]
     public virtual List<ApplicationUser>? Users { get; set; }
}

2. 正确处理多对多集合的更新

不要直接替换集合,先清除现有关联,再添加被上下文追踪的语言实体:

var user = await _context.Users
    .Include(u => u.Languages) // 加载现有语言关联
    .FirstOrDefaultAsync(u => u.Id == updateProfile.Id);

if (user != null)
{
    // 清除现有关联
    user.Languages.Clear();

    // 遍历需要添加的语言,确保实体被上下文追踪
    foreach (var lang in updateProfile.Languages)
    {
        var existingLang = await _context.Languages.FindAsync(lang.Id);
        if (existingLang != null)
        {
            user.Languages.Add(existingLang);
        }
    }

    // EF会自动追踪集合变更,无需手动设置EntityState.Modified
    await _context.SaveChangesAsync();
}

3. 重新生成迁移并更新数据库

执行以下命令修复数据库结构:

Add-Migration FixLanguageRelationship
Update-Database

额外说明

  • 多对多关系的中间表主键是两个实体主键的组合,重复插入同一用户-语言对会触发主键冲突。
  • 如果updateProfile.Languages是前端传入的数据,只需要包含Id字段即可,避免EF将其识别为新实体尝试插入新的Languages记录。

内容的提问来源于stack exchange,提问作者Dave Koppe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:24:52