Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix "EntityMemberChanged was called without first calling EntityMemberChanging"

This came up time and again for us. After reading such a message, there's nothing intuitive to do and debug.

like image 533
jnm2 Avatar asked Dec 18 '13 15:12

jnm2


2 Answers

What this poorly-documented error is trying to say is that you accidentally set up a system in which tracking changes causes more changes.

When Entity Framework changed a property on one of your entities, such as during SaveChanges with identity ID updates, you ran code that changed other tracked properties.

For example, the property that Entity Framework was setting triggered an event, perhaps INotifyPropertyChanged, which perhaps was subscribed to by a BindingSource or some binding list, whose ListChanged event handler was in the UI and triggered a calculation of some other property, and the change tracker detected the second property change.

The simple diagnosis is to place a breakpoint on the SaveChanges() call and immediately after the SaveChanges call(). When the first breakpoint is hit, place a breakpoint on each event handler that could possibly be triggered. (BindingSources are notorious for multiplying each other's events.) Continue debugging. If any breakpoint is hit other than the point immediately following SaveChanges, you know where the problem is.

The simple solution is to set a flag, such as IsSaving, on each side of the SaveChanges call. Then in each misbehaving event handler, do a simple check and do not modify any entities if the DbContext is in the process of saving. Make sure you use finally in case SaveChanges throws an exception that you catch at a higher level:

IsSaving = true;
try
{
    await db.SaveChangesAsync()
}
finally
{
    IsSaving = false;
}

(One other possibility is that you were changing the entity from multiple threads — never involve the change tracker in multiple threads!)

like image 174
jnm2 Avatar answered Oct 13 '22 11:10

jnm2


I had the exact same issue. I had wired to the INotifyPropertyChanged event that created the possibility for a property to change during the SaveChanges() call. I think it is a better practice to unwire the event handlers of you tracked entities when performing dbContext.SaveChanges(), Remove().

like image 20
Mathew Gehres Avatar answered Oct 13 '22 12:10

Mathew Gehres