Reputation: 10604
Basically, I have a table which contains a few properties for a company. This is the "master" table and their ID is used in many other tables. I basically find their ID via this method:
private Company currentcompany()
{
Company cuco = db.Companies.Single(x => x.username == User.Identity.Name);
return cuco;
}
I need to give users the ability to update various details about themselves stored in this table, which I did perfectly well - however, I noticed a big security hole!
Using Tamper Data on Firefox (And I imagine Fidler/many others), I could easily change the hidden ID and modify another companies details.
To stop this, I added the following lines to the modify action:
Company cuco = currentcompany();
if (company.id != cuco.id)
{
return Content("Security Error");
}
(FYI - Company
is a model/POCO representing a company, and company
itself is the form data.)
After adding this, if I edit the ID in the form data, it works as expected and brings up "Security Error", however, if there isn't an error and I go on, I get the error in the question.
"An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."
I believe this is because EF is somehow detecting and keeping the first data pull, but I am just un sure on how to correct it.
Any advice?
edit- --update--
If you can understand what I am trying to achieve, is there a better way of going around this?
Upvotes: 60
Views: 57920
Reputation: 3308
As the error clearly states - you need to make sure to get existing item and modify that item data with updated information and save it. You will not run into this issue if you update very specific information and not key information such as ID and ForeignKey ID
Below code should do the job!
public virtual void Update(TEntity entity)
{
_context.Entry(entity).State = EntityState.Modified;
this._context.SaveChanges();
}
Usage will be -
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);
...
Upvotes: 1
Reputation: 30208
Just a bit of help for you if you don't know how to find the oldEntity
as according to Ladislav:
var entityKey = context.NewEntitySet.Create().GetType().GetProperty("Id").GetValue(newEntity);
factory.Entry(context.Set<NewEntityType>().Find(entityKey)).CurrentValues.SetValues(newEntity);
Upvotes: 5
Reputation: 364269
If you load the entity from the context you cannot attach an entity with the same key again. The first entity is still kept in internal context cache and context can hold only one instance with given key value per type (it is called identity map and I described it here in other situation).
You can solve it by detaching former instance but you don't have to. If you only need to save new values you can use this:
context.YourEntitySet.ApplyCurrentValues(newEntity);
context.Entry(oldEntity).CurrentValues.SetValues(newEntity);
Upvotes: 150
Reputation: 4283
Just saw this issue yesterday. You need to detach cuco before the update. Check out the solution in this answer
Upvotes: 0