2

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.

Tot Zam
  • 8,406
  • 10
  • 51
  • 76
  • Are you sure currentvalues doesn't contain soemthing added to the context, but not saved to the db – Steve Jun 20 '17 at 15:21
  • @Steve What do you mean by that? I am not that familiar with these Entity Framework functions, so I might just be missing something simple. The audit log recording always takes place before `base.SaveChanges()` is called, and that never seems to be a problem. Is that what you are referring to? – Tot Zam Jun 20 '17 at 15:32
  • Have you Tried attaching your dbSet? – Dhouha BOUSSALEM Jun 20 '17 at 15:39
  • @user3801869 What does attaching a DbSet do? Where would I implement that? – Tot Zam Jun 20 '17 at 15:41
  • Where do you get the `DbEntityEntry Modified` passed to the method in question? Also at the beginning, what's the value of the `Modified.State` property for the entity which is failing? – Ivan Stoev Jun 20 '17 at 15:46
  • The error told "because the entity does not exist in the context.". Your modified entry is not attached to your DbContext. so before calling dbContext.SaveChanges(); you have to do: dbContext.MyTable.Attach(Modified); – Dhouha BOUSSALEM Jun 20 '17 at 15:52
  • @IvanStoev I've added some more code to my question. The `Modified.State` is `EntityState.Modified`. – Tot Zam Jun 20 '17 at 16:02
  • That's strange. Could be some side effect of `Reload`. You said it's happening only if you do a specific update. Can you provide repro (doesn't need to be a real code, just something that can be run and produces the exception - basically `mcve`)? – Ivan Stoev Jun 20 '17 at 16:31
  • 1
    Depending on your code structure, something like this would happen if you have more than one DbContext being instantiated (I.e. one per repository or per service) and an instance of an entity has been handed off from one DbContext to another. I'd suspect this is a case where the MyTable is potentially a child or reference on a top level entity being saved? Adding a check on the Modified.State for EntityState.Detached would be a good starting point for identifying where an entity might be slipping in. Attaching it should fix the issue, but might be worth investigating how it got that way. – Steve Py Jun 21 '17 at 05:29
  • @StevePy Thanks for the ideas! What you mentioned wasn't the exact problem, but your points helped me start thinking in the right direction. – Tot Zam Jun 21 '17 at 17:58

2 Answers2

3

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:

  1. Pass in the ID of the object to be deleted.
  2. Get the list of MyTable objects displayed in the HTML table, excluding the object with the ID set to be deleted.
  3. Loop through all the objects and save any modifications, like changes to the Name field.
  4. Delete the 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.

Tot Zam
  • 8,406
  • 10
  • 51
  • 76
0

In my case, the problem was related to AsNoTracking (offering an instance of Entity normally by Load , but when I changed it to AsNoTracking the update start to trigger an excepction because the Entity delivered is disconnected of the Entity Framework)

MCunha98
  • 81
  • 3
  • 12