Ask a questionAsk a question
 

AnswerInvalidOperationException on ObjectContext.AcceptAllChanges

  • Tuesday, June 30, 2009 11:33 AMGraham Hay Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,
    I am encountering an InvalidOperationException from a call to ObjectContext.AcceptAllChanges().

    The sequence of steps in the code is:

    1. Create/Update some entities

    2. Start a new EntityTransaction on the EntityConnection

    3. Call ObjectContext.SaveChanges(false)

    4. Do some GetObjectByKey/Linq queries (for logging purposes)

    5. Call ObjectContext.AcceptAllChanges()   --> throws InvalidOperationException

    ...


    Exception Info
    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."

    Source
    "System.Data.Entity"

    Stack Trace
       at System.Data.Objects.ObjectStateManager.FixupKey(EntityEntry entry)
       at System.Data.Objects.ObjectStateEntry.AcceptChanges()
       at System.Data.Objects.ObjectContext.AcceptAllChanges()
       at MyEntityModel.EntityContext.Save() in

    Data
    Empty -  Data {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal}


    If I remove step 4, then the ObjectContext.AcceptAllChanges() executes successfully with no exception.
    Since the Data property is empty, I cannot tell which entities are causing the problem.

    So, my question is:

    Is my code that invokes GetObjectByKey/Linq queries in step 4 expected to cause an exception in AcceptAllChanges() so I should restructure my code
    or
    is this exception unexpected indicating that there is something I am doing in step 4 that is invalid in this situation and that if I can avoid the invalid operation, then my code should work ?


    Thanks in advance
    Graham


Answers

  • Wednesday, July 01, 2009 4:35 AMDaniel Simmons - MSFTOwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Well, if your LINQ queries in step 4 retrieve one or more of the entities that were new this session and saved to the database in step 3, then you have a problem.  Entities which are in the added state have temporary EntityKeys which means that no query will identity resolve to them.  In your case, because you do SaveChanges(false), the new entities are saved to the DB but kept in the added state.  So your queries can cause an additional copy of the same entity to end up in the ObjectStateManager.  Then, when the call is made to AcceptAllChanges, the system does an AcceptChanges on the new entity which turns its  key from a temp key to a real one that conflicts with the key on the extra copy of that entity.

    The workaround I'd recommend is to use NoTracking for your linq queries so that you can accomplish the logging without adding other copies of the entities to the context.

    - Danny
    This posting is provided "AS IS" with no warranties, and confers no rights.
    • Marked As Answer byGraham Hay Wednesday, July 01, 2009 9:38 AM
    •  

All Replies

  • Wednesday, July 01, 2009 4:35 AMDaniel Simmons - MSFTOwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Well, if your LINQ queries in step 4 retrieve one or more of the entities that were new this session and saved to the database in step 3, then you have a problem.  Entities which are in the added state have temporary EntityKeys which means that no query will identity resolve to them.  In your case, because you do SaveChanges(false), the new entities are saved to the DB but kept in the added state.  So your queries can cause an additional copy of the same entity to end up in the ObjectStateManager.  Then, when the call is made to AcceptAllChanges, the system does an AcceptChanges on the new entity which turns its  key from a temp key to a real one that conflicts with the key on the extra copy of that entity.

    The workaround I'd recommend is to use NoTracking for your linq queries so that you can accomplish the logging without adding other copies of the entities to the context.

    - Danny
    This posting is provided "AS IS" with no warranties, and confers no rights.
    • Marked As Answer byGraham Hay Wednesday, July 01, 2009 9:38 AM
    •  
  • Wednesday, July 01, 2009 9:38 AMGraham Hay Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hi Danny,

    armed with your very clear explanation, I have restructured my code and I am now able to avoid the problem.

    Thanks for your help

    Graham

     

  • Wednesday, November 04, 2009 2:24 PMLopezan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi.

    I have the same problem using savechanges() method. The first time works, from the second time show the error  Can you help me?
  • Wednesday, November 04, 2009 4:12 PMDaniel Simmons - MSFTOwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Is it really the same as above?  That is, are you calling SaveChanges(false) and then calling AcceptAllChanges() or is it something else where you are calling SaveChanges() twice?  Also, what exactly is the exception message (which exception type and what message)?

    - Danny
    This posting is provided "AS IS" with no warranties, and confers no rights.
  • Friday, November 06, 2009 11:03 AMLopezan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Daniel, this is my problem:

    ----------------------------

    I use VS2008 SP1 and .NET 3.5.

    I have created an EF model from an existing DB with only one table like the following:

    ID INTEGER
    CODE VARCHAR
    DESCRIPTION VARCHAR

    This table has an ID (primary key) with a trigger that autogenerate his values.
    I have bind this table to a gridview.
    After every insert,change or delete row, I call the savechanges method to save data.

    But I have this problem.
    The first time I insert a row, all works fine.
    The next times, the savechanges method raise the exception:

    InvalidOperationException

    and if I go on, the last row is saved but also the previous rows are saved again.

    So, using the grid, if I insert the rows with this value:

    CODE DESCRIPTION
    1 1
    2 2
    3 3
    4 4
    5 5

    in the table I have:

      ID CODE DESCRIPTION
      1 1                1                                                                                                    
      2 2                2                                                                                                    
      3 2                2                                                                                                    
      5 2                2                                                                                                    
      9 2                2                                                                                                    
      4 3                3                                                                                                    
      6 3                3                                                                                                    
      8 3                3                                                                                                    
      7 4                4                                                                                                    
     11 4               4                                                                                                    
     10 5               5                                                                                                    


    Finally, the ID is generated correctly but the rows are duplicated.
    Please, help me.

    Regards.
    Angelo.