10
votes

I am having a problem with Entity Framework 4.0. I am trying to save a "Treatment" object that has a collection of "Segment" objects. Whenever I try to add/edit a Treatment object where I am adding 2 or more new Segments, I get the following error:

The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.

Here is the save method I am using. The "SegmentID" column is the PK for a "Segment" and it is an integer that is set to auto increment in the DB (MS SQL 2008). By default, the "SegmentID" is set to 0 until it gets the updated segment from the DB.

public bool Save(Treatment myTreatment)
    {
        bool result = false;


        using (tamcEntities db = new tamcEntities())
        {
            // IF NEW TREATMENT, CREATE IT AND ADD TO DB
            if (myTreatment.Treatment_ID == 0)
            {

                db.Treatments.AddObject(myTreatment);
                result = (db.SaveChanges() != 0);

            }
            // IF EXISTING TREATMENT, FIND EXISTING TREATMENT IN DB, AND UPDATE IT
            else
            {
                List<string> treatmentIncludes = new List<string>();
                treatmentIncludes.Add("Segments");

                Treatment myTmt = (from x in db.Treatments
                                   where x.Treatment_ID == myTreatment.Treatment_ID
                                   select x).WithIncludes(treatmentIncludes).FirstOrDefault();

                if (myTmt != null)
                {

                    myTmt.Comment = myTreatment.Comment;
                    myTmt.Cost = myTreatment.Cost;
                    myTmt.CostItemDrain = myTreatment.CostItemDrain;
                    myTmt.CostItemE2E = myTreatment.CostItemE2E;
                    myTmt.CostItemEnhan = myTreatment.CostItemEnhan;
                    myTmt.CostItemEnv = myTreatment.CostItemEnv;
                    myTmt.CostItemGuard = myTreatment.CostItemGuard;
                    myTmt.CostItemOther = myTreatment.CostItemOther;
                    myTmt.CostItemPed = myTreatment.CostItemPed;
                    myTmt.CostItemSub = myTreatment.CostItemSub;
                    myTmt.CostItemTraffic = myTreatment.CostItemTraffic;
                    myTmt.CostItemUtl = myTreatment.CostItemUtl;
                    myTmt.Create_DateTime = myTreatment.Create_DateTime;
                    myTmt.Create_Entity = myTreatment.Create_Entity;
                    myTmt.Create_User = myTreatment.Create_User;
                    myTmt.Description = myTreatment.Description;
                    myTmt.Improvement_Type = myTreatment.Improvement_Type;
                    myTmt.Jurisdiction = myTreatment.Jurisdiction;
                    myTmt.Last_Update_DateTime = myTreatment.Last_Update_DateTime;
                    myTmt.Last_Update_Entity = myTreatment.Last_Update_Entity;
                    myTmt.Last_Update_User = myTreatment.Last_Update_User;
                    myTmt.Life_Expectancy = myTreatment.Life_Expectancy;
                    myTmt.MDOTJobID = myTreatment.MDOTJobID;
                    myTmt.Planned = myTreatment.Planned;
                    myTmt.Project_Classification = myTreatment.Project_Classification;
                    myTmt.ProjectID = myTreatment.ProjectID;
                    myTmt.Quantity = myTreatment.Quantity;
                    myTmt.SurfaceTypeAfter = myTreatment.SurfaceTypeAfter;
                    myTmt.tmp_treat = myTreatment.tmp_treat;
                    myTmt.Treatment_Date = myTreatment.Treatment_Date;
                    myTmt.Unit_of_Measure = myTreatment.Unit_of_Measure;



                    // DELETE MISSING SEGMENTS THAT ARE NO LONGER PART OF THE TREATMENT
                    List<int> segmentIDsToKeep = myTreatment.Segments.Select(x => x.SegmentID).ToList();
                    myTmt.Segments.Where(x => !segmentIDsToKeep.Contains(x.SegmentID)).ToList().ForEach(x => db.Segments.DeleteObject(x));


                    // ITERATE OVER EACH SEGMENT AND INSERT OR UPDATE IT
                    foreach (Segment s in myTreatment.Segments)
                    {

                        if (!string.IsNullOrWhiteSpace(s.PR) && !string.IsNullOrWhiteSpace(s.BMP.ToString()) && !string.IsNullOrWhiteSpace(s.EMP.ToString()))
                        {
                            Segment mySegment = new Segment();

                            // IF EXISTING SEGMENT, FIND EXISTING SEGMENT IN DB, AND UPDATE IT
                            if (s.SegmentID != 0)
                            {
                                mySegment = (from x in myTmt.Segments
                                             where x.SegmentID == s.SegmentID
                                             select x).FirstOrDefault();
                            }

                            mySegment.ActualLength = s.ActualLength;
                            mySegment.BMP = s.BMP;
                            mySegment.Create_DateTime = s.Create_DateTime;
                            mySegment.Create_Entity = s.Create_Entity;
                            mySegment.Create_User = s.Create_User;
                            mySegment.EMP = s.EMP;
                            mySegment.HasRequiredHPMS = s.HasRequiredHPMS;
                            mySegment.Lanes = s.Lanes;
                            mySegment.Last_Update_DateTime = s.Last_Update_DateTime;
                            mySegment.Last_Update_Entity = s.Last_Update_Entity;
                            mySegment.Last_Update_User = s.Last_Update_User;
                            mySegment.PASER_Rating = s.PASER_Rating;
                            mySegment.PR = s.PR;
                            mySegment.RoadName = s.RoadName;
                            mySegment.SurfaceType = s.SurfaceType;
                            mySegment.Treatment_ID = s.Treatment_ID;
                            mySegment.Version = s.Version;

                            // If the BMP is greater than the EMP, swap them.
                            if (mySegment.BMP > mySegment.EMP)
                            {
                                decimal tempBMP = mySegment.BMP;
                                decimal tempEMP = mySegment.EMP;

                                mySegment.BMP = tempEMP;
                                mySegment.EMP = tempBMP;
                            }


                            // IF NEW SEGMENT, ADD IT
                            if (s.SegmentID == 0)
                            {
                                myTmt.Segments.Add(mySegment);
                            }



                        }

                    }

                    result = (db.SaveChanges(SaveOptions.AcceptAllChangesAfterSave) != 0);
                }

            }



        }

        return result;
    }
4
May sound silly but in our case there was an unnecessary select statement in a trigger that was making the trigger return data and the SaveChanges() actually took quite some time to execute and eventually thrown the above error. - pavan kumar

4 Answers

5
votes

I have same error when inserting rows into Oracle table, solved editing edmx file as xml (right click, open with.., XML Editor) and added StoreGeneratedPattern="Identity" to table definition.

Before

<Property Name="ID" Type="number" Nullable="false" Precision="38" Scale="0" />

After

<Property Name="ID" Type="number" Nullable="false" Precision="38" Scale="0" StoreGeneratedPattern="Identity" />
2
votes

The issue is you are assigning the same Segment key twice in your context, which mucks up the ObjectStateManager.

myTreatment has a collection of Segment entities, all of which are being tracked. Now, as you loop through them, you create another Segment which could end up having the same key as an existing Segment in your collection:

foreach (Segment s in myTreatment.Segments){
    ....
    Segment mySegment = new Segment(); //NEW OBJECT

    if (s.SegmentID != 0)
    {
        //IN HERE YOU ASSIGN THE SAME KEY TO THE NEW OBJECT
        //s.SegmentID == mySegment.SegmentID **CONFLICT**
        mySegment = (from x in myTmt.Segments
                    where x.SegmentID == s.SegmentID
                    select x).FirstOrDefault();
    }         
0
votes

The error shows if you assign a column as unique and try to save same value to that column. I can give you hint if it is feasible for you. For this to work, you can add a new auto incremented column named 'id' in the segments table and treat this column as a key

table: segment
id- new auto incremented key column
treatment_id
segment_id

entity:
id- key
treatment_id
segment_id

Now do your add operation. And do your edit and delete operation of segment entity based on the id of segment.

0
votes

I faced this issue recently, it was because i added a bulk of objects with default ID of zero, expecting that the database and the entity framework would be able to calculate the correct ID since I added a sequence and a trigger to database table. But, seems that it did not, I had to prepare the object with the correct ID sequence before saving changes to database context.

lookupOperation.ID = Sequance.GetSequenceBySequenceName("lo_SEQ");