Entity Framework Invalid Operation Exception – a bug or developer’s bad design?

One day one team had some strange problem while adding certain entities to the database through Entity Framework. Each time, they got an exception telling that, “The changes to the database were committed successfully, but an error occurred while updating the object context.” After some analyzing I created a sample project which would serve as an example while explaining what the origin of their problem was. In this post, I will explain a bit of how the Entity Framework manages entity state and what caused the problem.

First of all we have to simulate the environment that the team had problem with.

Obviously, I didn’t dive deeply into their logic, but I was able to make a basic model of their system. The application should work in the following way:  take some entity from the database, execute stored procedure passing it’s ID. The stored procedure makes some logic and physically removes the entity from the database. After that a new entity with the same ID is added directly from managed code to the context.

To simulate the project I created a simple table with three columns and stored a procedure that just removes the row with a specified ID. The ID is an uniqueidentifier T-SQL type(.NET Guid). Secondly, I created a sample code that threw the same exception like in the original project.

[csharp]
Guid id = Guid.NewGuid();
using (var context = new SambleDbEntities())
{
var newVelocityEntity = new Velocity()
{
Id = id,
VelocityValue = 1
};

context.Velocity.AddObject(newVelocityEntity);
context.SaveChanges();

context.MakeSomethingAndRemoveFromStoredProc(id);

var secondVelocityEntity = new Velocity
{
Id = id,
VelocityValue = 2
};
context.Velocity.AddObject(secondVelocityEntity);
context.SaveChanges();
}
[/csharp]

The InvalidOperationException occurred when SaveChanges was called, telling that

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.

It tells you in a clear way what’s wrong, but to understand it well we should know how EF framework works from the inside. To spare time and the whole complexity let’s briefly go through the EF logic.

How does EF store the data

Our SimpleDbEntities inherits from ObjectContext, which is responsible for the whole communication between our classes and the database. It contains the ObjectStateEntry “mechanism”, responsible for checking what changed in objects and how to retrieve and store the data.

The ObjectStateManager maintains identity and state for instances. Let’s take a deeper look  into this by checking the HashCodes of all objects. I created a helper method that checks the HashCode of the object (HashCode returns a kind of object ID in the runtime identifying the object), value that is stored in the entity and short info that should be displayed informing at what step are we checking the object.

[csharp]
private static void DisplayEntityInfo(Velocity velocity, string note)
{
Console.WriteLine("{0} | {1} | {2}",
velocity.GetHashCode(),
velocity.VelocityValue,
note);
}
[/csharp]

Now by running the sample code

[csharp]
Guid id = Guid.NewGuid();
using (var context = new SambleDbEntities())
{
var newVelocityEntity = new Velocity()
{
Id = id,
VelocityValue = 1
};

DisplayEntityInfo(newVelocityEntity, "newVelocityEntity");

context.Velocity.AddObject(newVelocityEntity);
context.SaveChanges();

var velocityFromContext = context.Velocity
.First(x => x.Id == id);
DisplayEntityInfo(velocityFromContext, "velocityFromContext");

velocityFromContext.VelocityValue = 3;

var thirdFromContext = context.Velocity
.FirstOrDefault(x => x.Id == id);
DisplayEntityInfo(velocityFromContext, "var thirdFromContext");

context.SaveChanges();
}
[/csharp]

as a result we get this information

[text]
19976800 | 1 | var newVelocityEntity = new Velocity()
19976800 | 1 | velocityFromContext
19976800 | 3 | var thirdFromContext
[/text]

The important note is that it returns always the same object from the context that was created at beginning, but how? The ObjectStateManager inside our context is responsible for this.

How to get rid-of the problem?

I will not write more about the ObjectStateManager here – first of all you should avoid such situations like the presented one in the sample code, where you are manipulating entity from code and stored procedure in the same context (for example when you switch back later to the project, you will probably forget what happened in this code) – but it can happen in some other situations. In our case we can omit the problem in a few ways:

  • By calling the Detach method of the data context, passing the entity retrieved at first

[csharp]
context.Detach(newVelocityEntity);
context.MakeSomethingAndRemoveFromStoredProc(id);
[/csharp]

[csharp]
context.SaveChanges(SaveOptions.DetectChangesBeforeSave);</code>
[/csharp]

But this option can be tricky – it will save the changes to the database without any error, but the context will not be refreshed and after you will retrieve entity from the context with the ID key, you will get the old values not your last updated.

  • Instead of creating a new entity, you can use the existing one and change its state to Added

[csharp]context.ObjectStateManager
.ChangeObjectState(newVelocityEntity, EntityState.Added);[/csharp]

Are you using Entity Framework? Do you have any other solutions/ideas how to fix such an exception? Maybe you are using some other ORM, where the problem can be fixed in a different way?

Tags:

6 comments

  1. thank you for solution.
    my prolbem is solve with “Calling SaveChanges with DetectChanges option” way!
    thank you thank you thank you very much!

  2. This does not work for EF 5.0 which has DBContext instead of the ObjectContext. Should the context.ChangeTracker.DetectChanges() be called before or after individual context.SaveChanges()? Please suggest. Please provide sample code wrt EF 5.0

  3. This does not work for EF 5.0 which has DBContext instead of the ObjectContext. Should the context.ChangeTracker.DetectChanges() be called before or after individual context.SaveChanges()? Please suggest. Please provide sample code wrt EF 5.0

    Also, I would like to point out that the code works perfectly fine when used WITHOUT stored procedures but FAILS when using stored procedure. I have SP for insert update and delete

    1. Hi, thanks for the question. Right now I am not able to prepare an example for EF 5. The latest versions of EF may work differently and during the time lots of changes have been introduced to EF. DetectChanges in my opinion should be called before SaveChanges always.
      You can access ObjectContext using only the DbContext – please try this piece of code ((IObjectContextAdapter)context).ObjectContext

Comments are closed.