ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象

时间:2011-05-17 15:54:47

标签: entity-framework

基本上,我有一张表,其中包含公司的一些属性。这是“主”表,它们的ID在许多其他表中使用。我基本上通过这种方法找到他们的ID:

private Company currentcompany()
    {
        Company cuco = db.Companies.Single(x => x.username == User.Identity.Name);
        return cuco;
    }

我需要让用户能够更新存储在此表中的各种有关自己的详细信息,我做得非常好 - 但是,我注意到了一个很大的安全漏洞!

在Firefox上使用篡改数据(我想象Fidler /其他许多人),我可以轻松更改隐藏的ID并修改其他公司的详细信息。

为了阻止这种情况,我在修改操作中添加了以下几行:

        Company cuco = currentcompany();

        if (company.id != cuco.id)
        {
            return Content("Security Error");
        }

(仅供参考 - Company是代表公司的模型/ POCO,company本身就是表单数据。)

添加后,如果我在表单数据中编辑ID,它会按预期工作并显示“安全错误”,但是,如果没有错误,我继续,我在问题中得到错误

“ObjectStateManager中已存在具有相同键的对象.ObjectStateManager无法使用相同的键跟踪多个对象。”

我相信这是因为EF以某种方式检测并保持第一次数据拉动,但我不确定如何纠正它。

有什么建议吗?

编辑 - --update -

如果你能理解我想要实现的目标,那么有更好的解决方法吗?

4 个答案:

答案 0 :(得分:149)

如果从上下文加载实体,则无法再次使用相同的键附加实体。第一个实体仍保留在内部上下文缓存中,上下文只能包含一个具有每个类型给定键值的实例(在其他情况下称为身份映射和I described it here)。

您可以通过分离以前的实例来解决它,但您不必这样做。如果您只需要保存新值,可以使用:

  • ObjectContext API:context.YourEntitySet.ApplyCurrentValues(newEntity);
  • DbContext API:context.Entry(oldEntity).CurrentValues.SetValues(newEntity);

答案 1 :(得分:5)

根据Ladislav的说法,如果您不知道如何找到oldEntity,请给您一些帮助:

var entityKey = context.NewEntitySet.Create().GetType().GetProperty("Id").GetValue(newEntity);

factory.Entry(context.Set<NewEntityType>().Find(entityKey)).CurrentValues.SetValues(newEntity);

答案 2 :(得分:1)

由于错误明确指出 - 您需要确保获取现有项并使用更新信息修改该项数据并保存。如果您更新非常具体的信息而不是 ID ForeignKey ID 等关键信息,则不会遇到此问题
下面的代码应该可以胜任!

 public virtual void Update(TEntity entity)
    {
        _context.Entry(entity).State = EntityState.Modified;

        this._context.SaveChanges();
    }

用法将是 -

 public async Task<bool> UpdateVendorItem(string userId, Vendor modified)
    {

        try
        {
            var existing = await this.GetVendors().SingleOrDefaultAsync(a => a.Id == modified.Id);


            //Set updated info
            existing.VendorName = modified.VendorName;

            //Update address information
            existing.Address.AddressLine1 = modified.Address.AddressLine1;
            ... 

            await _vendorRepository.UpdateAsync(existing);

    ...

答案 3 :(得分:0)

昨天刚看到这个问题。您需要在更新之前分离cuco。查看此answer

中的解决方案