当前位置: 首页 > 知识库问答 >
问题:

在EF 6中更新存量数据会引发异常-"...相同类型的实体已经具有相同的主键值。"

宇文飞翮
2023-03-14

我正在尝试使用实体框架6、代码优先、无流畅映射或Automapper之类的工具更新记录。

实体(员工)具有与之关联的其他复合属性,例如Addreess(集合),部门

它还继承自一个名为User的基

save方法如下所示,dbContext是DbConext的实现

        public bool UpdateEmployee(Employee employee)
        {
            var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault();
            if (entity == null)
            {
                _dbContext.Employees.Add(employee);
            }
            else
            {
                _dbContext.Entry(employee).State = EntityState.Modified; // <- Exception raised here
                _dbContext.Employees.Attach(employee);

            }

            return _dbContext.SaveChanges() > 0;

        }

我不断收到错误:

附加类型的实体失败,因为另一个相同类型的实体已经具有相同的主键值。如果图形中的任何实体具有冲突的键值,则使用“附加”方法或将实体的状态设置为“未更改”或“修改”时可能会发生这种情况。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“修改”。

我尝试了以下方法:

  1. 在设置为实体状态之前附加。已修改
  2. 在查询对象是否存在时添加AsNoTracking()(无异常,但数据库未更新)-https://stackoverflow.com/a/23228001/919426
  3. 使用基本实体dbContext保存。用户,而不是员工实体-https://stackoverflow.com/a/25575634/919426

现在这些都不适合我。

在我的情况下,这些解决方案中的一些不起作用,我可能做错了什么?

共有3个答案

韩宜春
2023-03-14

在使用存储库和工作单元模式时,我遇到了同样的情况(如mvc4 with ef5教程中所述)。

GenericRepository包含一个Update(TEntity)方法,该方法尝试附加然后设置条目。状态=已修改。如果您打算坚持uow/回购模式,那么上面投赞成票的“答案”无法解决这一问题。

我确实尝试在附加之前使用分离过程,但由于最初问题中指出的相同原因,它仍然失败。

事实证明,这样做的原因是我在检查是否存在记录,然后在调用update()之前使用自动apper从我的dto生成一个实体对象。

通过检查该记录的存在,我将实体对象放在范围内,并且无法分离它(这也是初始提问者无法分离的原因)... Tt跟踪了记录,并且在我将dto自动化到实体中然后尝试更新后不允许任何更改。

以下是通用回购的更新实施:

public virtual void Update(TEntity entityToUpdate)
{
    dbSet.Attach(entityToUpdate);
    context.Entry(entityToUpdate).State = EntityState.Modified;
}

这是我的PUT方法(我正在使用WebApi和Angular)

[HttpPut]
public IHttpActionResult Put(int id, Product product)
{
    IHttpActionResult ret;
    try
    {
        // remove pre-check because it locks the record
        // var e = unitOfWork.ProductRepository.GetByID(id);
        //  if (e != null) {
        var toSave = _mapper.Map<ProductEntity>(product);
        unitOfWork.ProductRepository.Update(toSave);
        unitOfWork.Save();
        var p = _mapper.Map<Product>(toSave);
        ret = Ok(p);
        // }
        // else
        //    ret = NotFound();
    }
    catch (DbEntityValidationException ex)
    {
        ret = BadRequest(ValidationErrorsToMessages(ex));
    }
    catch (Exception ex)
    {
        ret = InternalServerError(ex);
    }
    return ret;
}

正如你所看到的,我已经注释掉了我的检查,以查看记录是否存在。如果我尝试更新一条不再存在的记录,我想我会看到它是如何工作的,因为我不再有NotFound()返回机会。

因此,为了回答最初的问题,我想说,在尝试之前不要寻找实体==null,或者想出另一种方法。也许在我的情况下,我可以在发现对象后处理UnitOfWork,然后进行更新。

芮学
2023-03-14

这对我自己有用

var aExists = _db.Model.Find(newOrOldOne.id);
if(aExists==null)
{
    _db.Model.Add(newOrOldOne);
}
else
{
    _db.Entry(aExists).State = EntityState.Detached;
    _db.Entry(newOrOldOne).State = EntityState.Modified;
}
上官正志
2023-03-14

EF已经包含了一种无需求助于Automapper即可映射属性的方法,假设您没有要更新的导航属性:

public bool UpdateEmployee(Employee employee)
    {
        var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault();
        if (entity == null)
        {
            _dbContext.Employees.Add(employee);
        }
        else
        {
            _dbContext.Entry(entity).CurrentValues.SetValues(employee);              
        }

        return _dbContext.SaveChanges() > 0;

    }

这通常会生成更好的SQL语句,因为它只会更新已更改的属性。

如果您仍然想使用原始方法,那么可以从上下文中删除实体,或者使用AsNoTracking(不确定为什么在您的情况下它没有更新,它应该没有效果,所以问题可能是其他原因),或者使用类似于bool exists=dbContext的方法修改查询,以防止它首先具体化实体。员工。任何(c=

 类似资料:
  • 错误: 其他信息:附加“实体”类型的实体失败。客户”,因为另一个相同类型的实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则使用“附加”方法或将实体的状态设置为“未更改”或“修改”时可能会发生这种情况。这可能是因为某些实体是新的并且没有收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体来跟踪图,然后根据需要将非新实体的状态设置为“未更改”或“修改”。 我的代码: 我是这

  • 我正在使用automapper重构一些代码,请参见下面注释掉的旧代码。 编辑:请注意,属性对象是从基础设置的,因此建议的重复问题不适用。 当状态设置为modified时,我得到以下异常:; 附加“M.Survey”类型的实体。ServiceLayer。模型属性“”失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“U

  • 错误消息:附加类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。 问题:我如何以类似于下面代码中AttachActivity方法所示的方式附加实体? 我必须假设上面错误消息的“另一个实体”部分指的是内存中存在但超出范围的对象(?)。我之所以注意到这一点,是因为我尝试附加的实体类型的DBSet的本地属性返回零。 我有理由相信实体不存在于上下文中,因为我遍历代码并在创建上下文时观察上下文。实

  • 如何修复错误... 更新记录显示此错误时: 显示错误:{“附加“DomainClass.WorkshopReport”类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到生成的数据库关键值。在这种情况下,请使

  • 使用AutoMapper时,出现以下错误: 正在附加“MyProject”类型的实体。达尔。用户“”失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。这可能是因为某些实体是新的,尚未收到数据库生成的键值。在这种情况下,请使用“Add”方法或“Added

  • 错误消息: 正在附加“FaridCRMData”类型的实体。模型。“Customer”失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的 我的代码: FactorService. BaseService.cs: