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

更新数据库条目时遇实体附加异常:同类型实体主键冲突

解决EF实体附加冲突:Attaching an entity of type 'ProgramLanguage' failed...

这个错误是Entity Framework里非常典型的上下文跟踪冲突问题——当你通过GetProgramLanguageById从数据库获取实体时,这个coreProgramLanguage已经被当前的UnitOfWork上下文跟踪了。之后你又创建了一个拥有相同主键的newCore实体,尝试更新时EF发现上下文里已经存在一个同主键的被跟踪实体,于是抛出了这个异常。

咱们先拆解下你代码逻辑里的冗余和问题点:

  • 你先获取了被上下文跟踪的coreProgramLanguage,接着把它转成DM再转回实体得到detachedCore——这一步完全没必要,纯粹绕了弯路
  • 然后将传入的业务模型转成newCore,赋值原有RecordDate和TestId后尝试更新
  • 此时newCore的主键和已被跟踪的coreProgramLanguage完全一致,EF不允许同一个上下文里存在两个同主键的被跟踪实体

解决方案一:直接修改已跟踪实体(最推荐)

既然coreProgramLanguage已经被上下文跟踪,直接把业务模型的字段值映射到这个实体上就好,EF会自动记录所有变更,不需要创建新实体。这种方式既简洁又从根源避免了跟踪冲突:

public bool UpdateProgramLanguage(ProgramLanguageDm programLanguage)
{
    using (var iUnitOfWork = new UnitOfWork<TourismEntities>())
    {
        var coreProgramLanguage = iUnitOfWork.CommonHandler.GetProgramLanguageById(programLanguage.ProgramLanguageId);
        if (coreProgramLanguage == null)
        {
            return false;
        }
        
        // 直接把业务模型的字段映射到已跟踪的实体,保留原有RecordDate和TestId
        coreProgramLanguage.ProgramLanguageName = programLanguage.ProgramLanguageName;
        coreProgramLanguage.ProgramLanguageLevel = programLanguage.ProgramLanguageLevel;
        
        // 如果你的Repository.Update只是标记实体为Modified,其实EF已经自动跟踪了变更
        // 如果UnitOfWork在Dispose前会自动执行SaveChanges,甚至可以省略Update调用
        iUnitOfWork.CommonHandler.UpdateProgramLanguage(coreProgramLanguage);
        return true;
    }
}

解决方案二:分离原有实体后附加新实体

如果你一定要用新创建的newCore实体,可以先把原来的coreProgramLanguage从上下文分离,再附加新实体并标记为修改状态:

public bool UpdateProgramLanguage(ProgramLanguageDm programLanguage)
{
    using (var iUnitOfWork = new UnitOfWork<TourismEntities>())
    {
        var coreProgramLanguage = iUnitOfWork.CommonHandler.GetProgramLanguageById(programLanguage.ProgramLanguageId);
        if (coreProgramLanguage == null)
        {
            return false;
        }
        
        // 把原有被跟踪的实体从上下文分离
        iUnitOfWork.Context.Entry(coreProgramLanguage).State = EntityState.Detached;
        
        var newCore = GeneralMigrate.ProgramLanguageDmToEntity(programLanguage);
        // 保留原有记录日期和TestId
        newCore.RecordDate = coreProgramLanguage.RecordDate;
        newCore.TestId = coreProgramLanguage.TestId;
        
        // 附加新实体并标记为修改状态
        iUnitOfWork.Context.Entry(newCore).State = EntityState.Modified;
        iUnitOfWork.SaveChanges(); // 假设你的UnitOfWork提供SaveChanges方法
        return true;
    }
}

解决方案三:获取不被跟踪的实体

在查询的时候使用AsNoTracking(),这样获取的实体不会被上下文跟踪,之后创建同主键的新实体就不会产生冲突:

首先修改你的GetProgramLanguageById方法(如果允许修改的话):

public ProgramLanguage GetProgramLanguageById(int id)
{
    return _context.ProgramLanguages.AsNoTracking().FirstOrDefault(p => p.ProgramLanguageId == id);
}

然后业务层代码可以保留类似原来的逻辑,但去掉多余的DM来回转换:

public bool UpdateProgramLanguage(ProgramLanguageDm programLanguage)
{
    using (var iUnitOfWork = new UnitOfWork<TourismEntities>())
    {
        var coreProgramLanguage = iUnitOfWork.CommonHandler.GetProgramLanguageById(programLanguage.ProgramLanguageId);
        if (coreProgramLanguage == null)
        {
            return false;
        }
        
        var newCore = GeneralMigrate.ProgramLanguageDmToEntity(programLanguage);
        newCore.RecordDate = coreProgramLanguage.RecordDate;
        newCore.TestId = coreProgramLanguage.TestId;
        
        return iUnitOfWork.CommonHandler.UpdateProgramLanguage(newCore);
    }
}

额外优化建议

你的模型迁移代码里,ProgramLanguageEntityToDm和ProgramLanguageDmToEntity可以简化,另外注意到DataModel里的RecordDate是非可空的,而DbModel是可空的,迁移时的?? new DateTime()处理是合理的,避免了空值问题。


附:你提供的相关代码格式化

DbModel:

public partial class ProgramLanguage 
{ 
    public int ProgramLanguageId { get; set; } 
    public int TestId { get; set; } 
    public string ProgramLanguageName { get; set; } 
    public string ProgramLanguageLevel { get; set; } 
    public Nullable<System.DateTime> RecordDate { get; set; } 
    public virtual Test Test { get; set; } 
}

DataModel:

[DataContract] 
[Serializable] 
public class ProgramLanguageDm 
{ 
    public ProgramLanguageDm() { Test = new Test(); } 
    [DataMember] public int ProgramLanguageId { get; set; } 
    [DataMember] public int TestId { get; set; } 
    [DataMember] public string ProgramLanguageName { get; set; } 
    [DataMember] public string ProgramLanguageLevel { get; set; } 
    [DataMember] public DateTime RecordDate { get; set; } 
    [DataMember] public Test Test { get; set; } 
}

数据库上下文交互代码:

public bool UpdateProgramLanguage(ProgramLanguage programLanguage) 
{ 
    _repository.Update(programLanguage); 
    return true; 
}

业务层代码(原版本):

public bool UpdateProgramLanguage(ProgramLanguageDm programLanguage) 
{ 
    using (var iUnitOfWork = new UnitOfWork<TourismEntities>()) 
    { 
        var coreProgramLanguage = iUnitOfWork.CommonHandler.GetProgramLanguageById(programLanguage.ProgramLanguageId); 
        if (coreProgramLanguage == null) { return false; } 
        var detachedCore = GeneralMigrate.ProgramLanguageDmToEntity(GeneralMigrate.ProgramLanguageEntityToDm(coreProgramLanguage)); 
        var newCore = GeneralMigrate.ProgramLanguageDmToEntity(programLanguage); 
        newCore.RecordDate = detachedCore.RecordDate; 
        newCore.TestId = detachedCore.TestId; 
        return iUnitOfWork.CommonHandler.UpdateProgramLanguage(newCore); 
    } 
}

模型迁移代码:

public static ProgramLanguageDm ProgramLanguageEntityToDm(ProgramLanguage entity) 
{ 
    return entity != null ? new ProgramLanguageDm
    { 
        TestId = entity.TestId, 
        ProgramLanguageId = entity.ProgramLanguageId, 
        ProgramLanguageLevel = entity.ProgramLanguageLevel, 
        ProgramLanguageName = entity.ProgramLanguageName, 
        RecordDate = entity.RecordDate ?? new DateTime(), 
    } : new ProgramLanguageDm(); 
}

public static ProgramLanguage ProgramLanguageDmToEntity(ProgramLanguageDm dm) 
{ 
    return dm != null ? new ProgramLanguage 
    { 
        TestId = dm.TestId, 
        ProgramLanguageId = dm.ProgramLanguageId, 
        ProgramLanguageLevel = dm.ProgramLanguageLevel, 
        ProgramLanguageName = dm.ProgramLanguageName 
    } : new ProgramLanguage(); 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 15:43:12