Reputation: 10679
I see that this question has been asked a lot, however I haven't found anything yet that solves the problem I'm having.
Obviously i'm using the Entity Framework to perform an update to a record. Once the updates are complete, however, whenever I try to save I get the following error message:
An object with the same key already exists in the objectstatemanager
At first I was passing in a collection object from the view that contained a copy of the the ZipCodeTerritory
model object zipToUpdate
. I changed the code by pulling this object out and just sending in the relevant fields instead. However, I'm still getting the same error.
What's also weird is the first time I run this code, it works fine. Any attempt after that I get the error.
Controller
Here is the code from the method calling the edit function
public static string DescriptionOnly(ZipCodeIndex updateZip)
{
if (!string.IsNullOrWhiteSpace(updateZip.newEffectiveDate) || !string.IsNullOrWhiteSpace(updateZip.newEndDate))
{
return "Neither effective or end date can be present if updating Territory Code only; ";
}
_updated = 0;
foreach (var zipCode in updateZip.displayForPaging.Where(x => x.Update))
{
ProcessAllChanges(zipCode, updateZip.newTerritory, updateZip.newStateCode, updateZip.newDescription, updateZip.newChannelCode);
}
_msg += _updated + " record(s) updated; ";
return _msg;
}
And here is the method that actually does the updating.
private static void ProcessAllChanges(ZipCodeTerritory zipToUpdate, string newTerritory, string newStateCode, string newDescription, string newChannelCode)
{
try
{
if (!string.IsNullOrWhiteSpace(newTerritory)) zipToUpdate.IndDistrnId = newTerritory;
if (!string.IsNullOrWhiteSpace(newStateCode)) zipToUpdate.StateCode = newStateCode;
if (!string.IsNullOrWhiteSpace(newDescription)) zipToUpdate.DrmTerrDesc = newDescription;
if (!string.IsNullOrWhiteSpace(newChannelCode)) zipToUpdate.ChannelCode = newChannelCode;
if (zipToUpdate.EndDate == DateTime.MinValue) zipToUpdate.EndDate = DateTime.MaxValue;
_db.Entry(zipToUpdate).State = EntityState.Modified;
_db.SaveChanges();
_updated++;
}
catch (DbEntityValidationException dbEx)
{
_msg += "Error during update; ";
EventLog.WriteEntry("Monet", "Error during ProcessAllChanges: " + zipToUpdate.ToString() + " |EX| " + dbEx.Message);
}
catch (Exception ex)
{
_msg += "Error during update; ";
EventLog.WriteEntry("Monet", "Error during ProcessAllChanges: " + zipToUpdate.ToString() + " |MESSAGE| " + ex.Message);
}
}
EDIT
The ZipCodeIndex
object contains a list of ZipCodeTerritory
model objects. These aren't being pulled from a linq query, but instead simply passed back to the controller from the view. Here is the signature of the controller method that starts the process:
[HttpPost]
public ActionResult Update(ZipCodeIndex updateZip, string button)
Upvotes: 2
Views: 8528
Reputation: 11362
When I have run into this type of problem - I haven't seen this exact one, but quite a few like it - it is because either I had retrieved my Entities with one context and I was trying to save them using another or because something had happened to cause Entity Framework to lose track of them - pulling them out of the datastore with GetAsNoTracking
will have this effect, although if you know that you aren't going to change the entities that provides a clear performance advantage.
The consequence of this is that EF thinks that you are saving a new Entity that happens to have most of the same attributes of one it already knows about, rather than making changes to the one it is familiar with.
From your code, I can't tell you what the exact solution is, but I would start troubleshooting this by paying close attention to where you are retrieving the entities and making sure that you are saving the changes back through the same context.
Your edit suggests that you are passing new ZipCodeTerritory
objects down from your view to the controller. These presumably map to existing ZipCodeTerritory
entries in your store, so what is happening here is that because they didn't come out of the database but they are entities of the correct type, the data context sees them as new objects. It doesn't know that you are updating existing records because it didn't create them so it holds no tracking reference to these objects that are coming back from the view.
There may be a more elegant solution to this now- I am currently working with 4.5 so I haven't had a look at more recent EF behaviour - but a simple solution would be to retrieve the matching ZipCodeTerritory
objects from your data store then update them and save them back. In this case although the objects you are passing back are of the ZipCodeTerritory
type they are behaving more like Data Transfer Objects, which is part of what creates the confusion here- because they were created by the view ( albeit from data that originated in the datastore ) your data context simply does not recognise them.
Upvotes: 0
Reputation: 2931
This is due to inproper disposal of the database context, as you are never disposing of the context object itself you will run into these kinds of problems.
I would suggest wrapping the code in a using statement..
using (var context = new MyContext())
{
// Do my save code here...
}
That will ensure proper disposal of the context!
Upvotes: 6