2

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.

Simon Elms
  • 17,832
  • 21
  • 87
  • 103

2 Answers2

3

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;
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
2

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.

Dennis VW
  • 2,977
  • 1
  • 15
  • 36
  • To add a little more info: I set `entity.Children = null;`, ie nulled the child collection, not each individual child entity. I also found I needed to do this before calling `context.Attach(entity);`. If I called context.Attach first then nulled the child collection, any new children were still inserted into the database. – Simon Elms Jan 04 '20 at 10:16
  • I think that setting state as in Ivan's answer is a lot safer than emptying navigation properties. That may result in persisted severed relationships if you don't know exactly what you're doing. Setting state is accurate and secure. – Gert Arnold Jan 04 '20 at 15:28
  • I agree. I wouldn't mind if @SimonTewsi would make Ivan's answer the accepted answer based on this. – Dennis VW Jan 04 '20 at 17:11
  • I've changed the accepted answer to Ivan's answer now. – Simon Elms Jan 15 '20 at 23:28