基本上,我有一张表,其中包含公司的一些属性。这是“主”表,它们的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 -
如果你能理解我想要实现的目标,那么有更好的解决方法吗?
答案 0 :(得分:149)
如果从上下文加载实体,则无法再次使用相同的键附加实体。第一个实体仍保留在内部上下文缓存中,上下文只能包含一个具有每个类型给定键值的实例(在其他情况下称为身份映射和I described it here)。
您可以通过分离以前的实例来解决它,但您不必这样做。如果您只需要保存新值,可以使用:
context.YourEntitySet.ApplyCurrentValues(newEntity);
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
中的解决方案