I have a MVC application that used Entity Framework v6.0. Whenever a change is made to the DbContext
, I override the SaveChanges()
method and record the modifications in an audit log. Below is code for part of the audit log recording process:
public override int SaveChanges()
{
var ChangesEntities = ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted
|| e.State == EntityState.Modified
|| e.State == EntityState.Added).ToList();
foreach (DbEntityEntry entry in ChangesEntities)
{
if (entry.State == EntityState.Modified)
{
SetCorrectOriginalValues(entry);
}
...
}
...
return base.SaveChanges();
}
void SetCorrectOriginalValues(DbEntityEntry Modified)
{
var values = Modified.CurrentValues.Clone();
Modified.Reload();
Modified.CurrentValues.SetValues(values);
Modified.State = EntityState.Modified;
}
This code usually works, however, when I attempt to make a specific update, I am getting the following error:
Exception Message:
Member 'CurrentValues' cannot be called for the entity of type 'MyTable' because the
entity does not exist in the context.
To add an entity to the context call the Add or Attach method of DbSet.
Exception Stack Track:
at System.Data.Entity.Internal.InternalEntityEntry.get_CurrentValues()
at System.Data.Entity.Infrastructure.DbEntityEntry.get_CurrentValues()
at MyDatabaseName.DataLibrary.MyDatabaseEntities
.SetCorrectOriginalValues(DbEntityEntry Modified)
at MyDatabaseName.DataLibrary.MyDatabaseEntities.SaveChanges()
...
What does the exception message mean? When and why would CurrentValues
not be able to be called? What does is mean that entity does not exist in the context?
In MyDatabaseEntities.Context.cs, which extends DbContext
, 'MyTable' seems to exist:
public virtual DbSet<MyTable> MyTables { get; set; }
I checked the Entity Framework model diagram, and 'MyTable' exists there to.
Thanks to the pointers given in the comments above, I was able to locate where the error was stemming from, and figured out what was triggering the error. The basic answer is that this was caused by a concurrency issue. I was trying to update a MyTable
object that had already been deleted in a previous call.
On my web page, I have a table of MyTable
object:
ID | Name | Delete Button
---- | ---- | -------------
1 | abc | button1
2 | xyz | button2
Each row has a delete button. When a button is clicked, the following actions occur:
ID
of the object to be deleted. MyTable
objects displayed in the HTML table, excluding the object with the ID
set to be deleted.Name
field.MyTable
object from the DbSet
that has an ID
that corresponds to the clicked button.The problem occurred when a second delete button was clicked before the first set of actions completed. When this happened, there were 2 sets of actions running simultaneously, and the order of events looked something like this:
Action 1: Click button1 (ID to delete = 1)
Action 1: Get the list of MyTable objects to update (includes ID 2)
Action 2: Click button2 (ID to delete = 2)
Action 2: Get the list of MyTable objects to update (still includes ID 1)
Action 1: Update all MyTable objects (ID 2)
Action 1: Delete ID 1
Action 2: Update all MyTable objects - ERROR since ID 1 has been deleted!
I ended up fixing the problem by rearranging the order of some of the events and adding a check to make sure an object still exists in the DbSet
and not just the HTML table, before attempting to update it.