Deleted entities show up as modified in Entity Framework Core's ChangeTracker - entity-framework

I'm trying to understand why Entity Framework Core 2's ChangeTracker sets the state of deleted entities to 'modified' if the deletion is done by removing the entity from a list in another entity.
Consider this example:
Class1
List<Class2> MyListOfClass2Objects
Class2
Both classes have their own DbSet in EF's DbContext. If I remove an object from the MyListOfClass2Objects list and then call DbContext.SaveChanges(), the state of the Class2 object in ChangeTracker is set to EntityState.Modified, and not EntityState.Deleted, which I would expect. If I delete the entity by calling Set<T>.Remove() the state in the ChangeTracker is EntityState.Deleted. In both cases however, the entity is removed from the database.
So, why does Entity Framework set different values in ChangeTracker depending on how you delete an entity even though it is really deleted in both cases?

Update: At the end (thanks to #Sebastian finding) it turns out to be a known issue with the current (at the time of writing) EF Core implementation, tracked originally by EF Core marks entities removed From child collections as modified #10093. The short explanation is:
because cascade deletes happens while SaveChanges is running which means in your code will not see this state as it looks at the state before SaveChanges has executed
and
Cascade delete happens as part of SaveChanges.
Fixup of non-deleted entities to no longer reference deleted entities happens after SaveChanges.
The currently associated action item is Allow delete fixup and cascade timing to be configured #10114, unfortunately in backlog, i.e. unknown if and when will be addressed.
Original:
Because when you remove an entity from the corresponding DbSet, you are telling EF that you want to delete it. But when you remove it from the parent entity collection, if the relationship is optinal (as it seems to be in your case), EF treats that as attempt to disassociate the child from the parent (same as setting child.Parent to null), hence it sets the FK and parent navgigation property to null and marks the entity as Modified. If you really want to delete it, you should use the first method.
All this is explained in the Removing relationships section of the documentation:
You can remove a relationship by setting a reference navigation to null, or removing the related entity from a collection navigation.
Removing a relationship can have side effects on the dependent entity, according to the cascade delete behavior configured in the relationship.
By default, for required relationships, a cascade delete behavior is configured and the child/dependent entity will be deleted from the database. For optional relationships, cascade delete is not configured by default, but the foreign key property will be set to null.

Related

set related childs in ef core to Entity State unchanged

I am using EF Core 5 and use for adding a new entity the Add Method. In the change tracker I saw that the entity state was set to added which is correct.
My Problem is now, if I want to add a new Entity and this new entity has already relations to other entities, this added Entity state is also set for the child entities, but they are already exist.
So is there an easy way to set all child entities with one method to the Entity State Unchanged or Modified?

Does Entity Framework Core have a simple way of preventing the update of child or parent entities?

I'm trying to write an UpdateStatus method which will only update the Status field of an entity when I save changes to the database. If any other fields in the entity have changed I don't want to save those changes to the database. That is simple enough for the entity's own fields, using:
using (var context = new DataAccessContext())
{
context.Attach(entity);
context.Entry(entity).Property(e => e.StatusCode).IsModified = true;
context.SaveChanges();
}
However, I've discovered that any related entity reachable via a navigation property of the entity I'm setting the status of will be inserted if that related entity does not have a key value set. So if a new Child entity is added to entity.Children by some calling code, and the Child entity ChildId property is 0, that Child will be inserted into the database.
Is there any easy way in EF Core to avoid inserting related entities?
I've found an old StackOverflow post that shows how to do it in the pre-Core Entity Framework: How do I stop Entity Framework from trying to save/insert child objects? However, that answer involves looping over every related entity. Is there an easier way in EF Core?
The reason I'm looking for an easier way is that my hierarchy of entities is 5 layers deep. And I've found that it's not enough to detach just the immediate children of an entity. You have to use nested loops to detach the grandchildren, the great-grandchildren, etc. If you only detach the immediate children they won't be inserted but EF Core will attempt to insert new grandchildren and will crash and burn because it hasn't inserted their parents. It gets pretty messy.
I could just read a fresh copy of an entity from the database before updating its Status but I'm trying to avoid having to do a read before I write.
What you are asking is quite simple in EF Core. If you don't want EF Core change tracker operation to process the related data, set the EntityEntry.State rather than calling DbContext / DbSet methods like Attach, Add, Update, Remove etc.
This behavior is different from EF6 where methods and setting state are doing one and the same, and is partially mentioned in the Saving Related Data - Adding a graph of new entities
documentation topic:
Tip
Use the EntityEntry.State property to set the state of just a single entity. For example, context.Entry(blog).State = EntityState.Modified.
So in your sample, simply replace
context.Attach(entity);
with
context.Entry(entity).State = EntityState.Unchanged;
Entity Framework Core ignores relationships unless you explicitly
include them in queries.
When attaching an entity to the database that has related data/ child properties, those entities will be included in the query.
So to fix this issue all you need to do is set those child properties to null and then EF Core will ignore the child-objects when you're updating the parent-object.

One to many relation : child is flagged Modified instead Deleted

I have some troubles using One to Many relationships with EntityFramework Core. When I delete a child object in the List property on the parent, this Child object is flagged as Modified by the ChangeTracker (and not Deleted). However, when I call SaveChanges on the context, this entity is well Deleted.
Of course, I could force the flag of the entity to Deleted but, I would prefer another solution because I'm using AutoMapper to update my entities. and I don't want to mix the AutoMapper mapping process and EntityFramework Context.
var parent = new Parent();
var child = new Child();
parent.Childs.Add(child);
await context.SaveChangesAsync();
// removing the first child
parent.Childs.RemoveAt(0);
// fails (Expected Deleted, got Modified)
Assert.Equal(EntityState.Deleted, context.Entry(child).State);
The best explanation of this behavior is contained inside one of the expected breaking changes in EF Core 3.0 - Cascade deletions now happen immediately by default:
Old behavior
Before 3.0, EF Core applied cascading actions (deleting dependent entities when a required principal is deleted or when the relationship to a required principal is severed) did not happen until SaveChanges was called.
New behavior
Starting with 3.0, EF Core applies cascading actions as soon as the triggering condition is detected. For example, calling context.Remove() to delete a principal entity will result in all tracked related required dependents also being set to Deleted immediately.
Why
This change was made to improve the experience for data binding and auditing scenarios where it is important to understand which entities will be deleted before SaveChanges is called.
The first section explains the current behavior and the last section explains why they are changing it - to help usage scenarios like yours.
With that being said, you should either apply the cascade option manually, or wait for EF Core 3.0 if you can afford that.

Deleting an Entity with related data and Cascading Deletes defined in SQLServer

I'm getting the following error when I SaveChanges after Removing an entity that has related entities containing data in the context as well. (The entity I'm deleting has the unique Primary Key). I have Cascading Delete configured at the SQL Server database level for the relation between the primary key table and the foreign key table.
"The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not su...
The primary entity has its related data loaded explicitly prior to me removing the primary from the context. I assumed that EF and SQL Server would take care of the cascaded delete for me. If the related entity has no data the delete (of the primary entity) works fine. If there is data in the related entity, I get the error above.
Any suggestions?
The lesson is that EF has a learning curve. Keep exploring.
Anyway, I was "deleting" entities by setting the state to "Deleted" then calling SaveChanges. This seems to work fine if the entity has no related data. However, if you have an entity that has related "child" entities, you need to call Remove on the parent entity for the "delete" to cascade through the graph. Live and learn. I'm pretty sure this is the answer. I think for the time being I'm going to stop setting State for deletes and use Remove instead.

DbContext's ChangeTracker problem

I have a codefirst EF-4.1 based program. The user gets a context and can modify some properties. When the user is done, I do a quick
ChangeTracker.Entries().Any(e => e.State != EntityState.Unchanged);
to determine if a SaveChanges() is required or not. If I do a 'SaveChanges()' call, the changes that have made are persisted to the database.
This works for some properties, and doesn't work for others. Specifically it seems to work with simple types (floats), and with collection hierarchies(ObservableCollections).
Am I doing something wrong?
Yes this is a problem. Some relations are not tracked by DbChangeTracker. There is difference between Independent association and Foreign key association. Changes to relation are tracked in case of:
One-to-one relation which is always Foreign key association in EFv4+
One-to-many relation with Foreign key association - you should set up foreign key property
Changes to relation are not tracked in case of:
One-to-many relation with Independent association
Many-to-many relation which is always Independent association
Not tracked for Independent association is not correct naming. These changes are tracked but DbChangeTracker does not expose access to these changes! You must convert DbContext to ObjectContext and use ObjectStateManager to get access to ObjectStateEntries representing independent associations.
In this case the easiest thing is simply call SaveChanges always. It will not execute any DB commands if no data need to be saved.