EF Core 1.1

Add, Attach, Update, and Remove methods

EF Core provides a variety of ways to start tracking entities or change their state. This post gives a brief overview of the different approaches.

Tracking queries

Queries will automatically track returned entities unless tracking has been turned off. If you want to track entities from a query, then just use this automatic tracking. Do not use a no-tracking query and then try to attach entities--doing so is slower and can require special handling for shadow properties.

Entity states

Tracked entities can be in one of four states. The state of an entity determines how it is processed when SaveChanges is called.

The Detached state is given to any entity that is not being tracked by the context.

Add, Attach, and Update methods

These methods are found on DbContext and also on DbSet. The DbContext and DbSet methods behave in exactly the same way.

All these methods work with the graph of untracked entities reachable from the entity passed to the method. So if you have a Blog which references two Posts and you call Add for the Blog, then the Posts will also be Added.

These methods do not automatically call DetectChanges. This is different from EF6 where these methods would automatically call DetectChanges.

Add

Add puts all entities in the graph into the Added state. This means they will be inserted into the database on SaveChanges.

Attach

Attach puts all entities in the graph into the Unchanged state. However, entities will be put in the Added state if they have store-generated keys (e.g. Identity column) and no key value has been set.

This means that when exclusively using store-generated keys, Attach can be used to start tracking a mix of new and existing entities where the existing entities have not changed. The new entities will be inserted while the existing entities will not be saved other than to update any necessary FK values.

(In EF Core 1.1 the root entity is special-cased such that it will always be marked as Unchanged even if it has a store-generated key with no value set. A fix for this has been checked in for the next release.)

Update

Update works the same as Attach except that entities are put in the Modified state instead of the Unchanged state.

This means that when exclusively using store-generated keys, Update can be used to start tracking a mix of new and existing entities where the existing entities may have some modifications. The new entities will be inserted while the existing entities will be updated.

Remove

Remove affects only the entity passed to the method. It does not traverse the graph of reachable entities.

If the entity is in the Unchanged or Modified state, indicating that it exists in the database, then it is put in the Deleted state.

If the entity is in the Added state, indicating that it has not yet been inserted, then it is detached from the context and is no longer tracked.

Remove is usually used with entities that are already being tracked. If the entity is not tracked, then it is first Attached and then placed in the Deleted state.

Range methods

The AddRange, AttachRange, UpdateRange, and RemoveRange methods work exactly the same as calling the non-Range methods multiple times. They are slightly more efficient than multiple calls to the non-Range methods, but the efficiency gain is very small because none of these methods now automatically call DetectChanges.

Setting entity state directly

The state of an entity can always be set directly using the DbContext.Entry method. For example:

context.Entry(blog).State = EntityState.Unchanged;

Setting the state this way:

TrackGraph

The TrackGraph method provides full control over the state set for entities in a graph. For example, imagine your entities all have integer keys called "Id", and that these keys are set to negative temporary values before they are inserted into the database. TrackGraph can then be used to set the state of each entity appropriately and inform EF that the negative values are temporary.

context.ChangeTracker.TrackGraph(blog, node =>
{
    var entry = node.Entry;

    if ((int)entry.Property("Id").CurrentValue < 0)
    {
        entry.State = EntityState.Added;
        entry.Property("Id").IsTemporary = true;
    }
    else
    {
        entry.State = EntityState.Modified;
    }
});

Using navigation properties

New entities can be tracked by setting them on a reference navigation property or by adding them to a collection navigation property of an entity that is already being tracked. In this case the new entities are always put in the Added state.

Usually DetectChanges does the job of finding these new entities. However, you can always use notification entities or use the context.Entry methods for references and collections, in which case DetectChanges is not needed,

Async Add

You might notice that there are async versions of the Add methods. It is very rare that you will need these. They are included so that client-side value generation that needs to access the database can do so asyncronously. Even then it is usually fine to use the synchronous methods.

Summary

EF Core provides a variety of different ways to start tracking entities or change their state. Most of the time the Add, Attach, Update, and Remove methods should be used. For more control, use TrackGraph coupled with setting the entity state directly.


This page is up-to-date as of November 17th, 2016. Some things change. Some things stay the same. Use your noggin.