ObjectStateManager 中已存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象
Posted
技术标签:
【中文标题】ObjectStateManager 中已存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象【英文标题】:An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key 【发布时间】:2011-08-27 09:06:02 【问题描述】:基本上,我有一张表,其中包含公司的一些属性。这是“主”表,它们的 ID 用于许多其他表。我基本上是通过这种方法找到他们的ID:
private Company currentcompany()
Company cuco = db.Companies.Single(x => x.username == User.Identity.Name);
return cuco;
我需要让用户能够更新存储在此表中的有关他们自己的各种详细信息,我做得非常好 - 但是,我发现了一个很大的安全漏洞!
在 Firefox 上使用 Tamper Data(我想象 Fidler/许多其他人),我可以轻松更改隐藏的 ID 并修改其他公司的详细信息。
为了阻止这种情况,我在修改操作中添加了以下几行:
Company cuco = currentcompany();
if (company.id != cuco.id)
return Content("Security Error");
(仅供参考 - Company
是代表公司的模型/POCO,company
本身就是表单数据。)
添加后,如果我在表单数据中编辑 ID,它会按预期工作并显示“安全错误”,但是,如果没有错误并且我继续,我会收到问题中的错误.
“ObjectStateManager 中已存在具有相同键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。”
我相信这是因为 EF 以某种方式检测并保留了第一个数据提取,但我不确定如何纠正它。
有什么建议吗?
编辑- --更新--
如果你能理解我想要达到的目标,有没有更好的方法来解决这个问题?
【问题讨论】:
仅供参考 - 我已经阅读了相关栏上的类似问题,但看不到解决方案。 :( 【参考方案1】:如果您从上下文加载实体,则无法再次附加具有相同键的实体。第一个实体仍然保存在内部上下文缓存中,并且每个类型只能保存一个具有给定键值的实例(在其他情况下称为身份映射和I described it here)。
您可以通过分离以前的实例来解决它,但您不必这样做。如果您只需要保存新值,您可以使用:
ObjectContext API:context.YourEntitySet.ApplyCurrentValues(newEntity);
DbContext API:context.Entry(oldEntity).CurrentValues.SetValues(newEntity);
【讨论】:
你太棒了...我认为你已经为我在这个网站上遇到的每个 EF 问题发布了一个很好的答案!我只是有点厌倦了问这么多 EF 问题,觉得我应该知道得更好!除了奇怪的博客和这里的问题,我找不到很多资源,只是想知道你是否可以推荐任何可以帮助我的东西? ...另外,对于这个实际问题,你会说做防伪令牌就足够了/在这种情况下你会怎么做?我一直认为客户的任何东西都应该被视为 不可信,如果不是这样,我检查数据库和比较 ID 的方法将完美运行! (而且,我将阅读有关这些 API 的信息,看看我是否可以在早上做点什么。 相信我,你不会问这么多问题。还有其他人打败你十次:) 忘记防伪令牌。它用于跨站点请求伪造防御 - 这部分我错了。如果你想在隐藏字段中传递 ID,你必须手动保护它(加密/解密)。 嗯......在现实生活中的应用程序中感到羞耻,它不像电影数据库/音乐商店样本那样简单!!!那么,从我在问题中给出的示例中,您是否会放弃所有数据库检查而只使用加密的 ID? (否则,我认为,如果/当我了解这些 api 时,这可能是最好的方法 - 正如我所说,它一开始似乎确实有效。) 没有 EFv4.1 的书。检查此答案中的链接以获取一些资源:***.com/questions/5974863/… 有几本非常好的 EFv4 书籍,但问题是 EFv4.1 提供了非常不同的 API - DbContext,而 EFv4 使用的是 ObjectContext API。【参考方案2】:如果您不知道如何按照 Ladislav 的说法找到 oldEntity
,对您有一点帮助:
var entityKey = context.NewEntitySet.Create().GetType().GetProperty("Id").GetValue(newEntity);
factory.Entry(context.Set<NewEntityType>().Find(entityKey)).CurrentValues.SetValues(newEntity);
【讨论】:
【参考方案3】:正如错误明确指出的那样 - 您需要确保获取现有项目并使用更新的信息修改该项目数据并保存。如果您更新非常具体的信息而不是 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);
...
【讨论】:
【参考方案4】:昨天刚看到这个问题。您需要在更新前分离 cuco。查看answer中的解决方案
【讨论】:
以上是关于ObjectStateManager 中已存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象的主要内容,如果未能解决你的问题,请参考以下文章
ObjectStateManager 中已存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象
ObjectStateManager 中已存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象
ObjectStateManager 中已存在具有同一键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象
无法附加分离的实体:“ObjectStateManager 中已存在具有相同键的对象”