使用EF7 & C#向类中列表添加现有项时触发重复键异常
EF7更新带联合主键的复杂类时重复键异常(23505)的解决办法
核心问题根源
EF7与EF6在处理分离状态下的关联实体追踪逻辑有差异:EF6会自动识别已存在的关联实体,仅更新关联关系;但EF7调用Update()时会递归将整个实体图(包括所有关联实体)标记为Modified,若关联实体的联合主键已存在于数据库,就会触发PostgreSQL的重复键错误(23505)。
具体解决方案
1. 手动标记关联实体为Unchanged
从数据库取出已存在的Address后,显式告诉EF这个实体不需要修改,只处理Patient与它的关联关系:
// 取出已存在的Address var existingAddress = context.Addresses.Find(targetTenantId, targetAddressEntityId); patient.Addresses.Add(existingAddress); // 标记Address为未修改状态 context.Entry(existingAddress).State = EntityState.Unchanged; context.Patients.Update(patient); context.SaveChanges();
2. 仅更新Patient实体本身
如果Patient的基础属性没有变化,只是集合属性更新,直接标记Patient为Modified即可,避免Update()递归处理关联实体:
var existingAddress = context.Addresses.Find(targetTenantId, targetAddressEntityId); patient.Addresses.Add(existingAddress); // 仅标记Patient为修改状态,不处理关联实体 context.Entry(patient).State = EntityState.Modified; context.SaveChanges();
3. 用TrackGraph批量控制追踪状态
如果有多个关联实体需要处理,使用TrackGraph自定义每个实体的追踪规则:
context.ChangeTracker.TrackGraph(patient, entry => { // 主实体Patient标记为修改 if (entry.Entry.Entity is Patient) { entry.Entry.State = EntityState.Modified; } // 关联的Address若已存在于库中,标记为未修改 else if (entry.Entry.Entity is Address address) { var exists = context.Addresses.Any(a => a.TenantID == address.TenantID && a.EntityID == address.EntityID); entry.Entry.State = exists ? EntityState.Unchanged : EntityState.Added; } }); context.SaveChanges();
4. 验证联合主键的关联配置
检查Fluent API的配置是否正确,确保外键包含联合主键的所有字段:
// 配置Patient的联合主键 modelBuilder.Entity<Patient>() .HasKey(p => new { p.TenantID, p.EntityID }); // 配置Address的联合主键 modelBuilder.Entity<Address>() .HasKey(a => new { a.TenantID, a.EntityID }); // 配置Patient与Addresses的关联,外键包含TenantID modelBuilder.Entity<Patient>() .HasMany(p => p.Addresses) .WithOne(a => a.Patient) // 若Address有Patient导航属性则填写,否则用.WithOne() .HasForeignKey(a => new { a.TenantID, a.PatientEntityID }); // 外键需包含TenantID
内容的提问来源于stack exchange,提问作者Pete Jenney
相关产品推荐
相关产品推荐

