Secrets of DetectChanges Part 1: What does DetectChanges do?

Entity Framework change tracking is often something that doesn’t require too much thought from the app developer. However, change tracking and the DetectChanges method are a part of the stack where there is a tension between ease-of-use and performance. For this reason it can be useful to have an idea of what is actually going on such you can make an informed decision on what to do if the default behavior is not right for your app.


Overview

I already blogged about change tracking proxies which touched on some areas of change tracking. In this four part series I’ll focus on the DetectChanges method.

  • Part 1 (this post) provides an introduction and describes briefly what DetectChanges does.
  • Part 2 details when DetectChanges is called automatically and what the consequences of these calls are.
  • Part 3 shows how to switch off automatic calls to DetectChanges and covers when you must then call it manually.
  • Part 4 looks at special considerations for DetectChanges when your entities contain complex types or byte arrays.

All the examples in this series will use the following simple Code First model and context.

public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }

    public virtual ICollection Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; }
}

public class AnotherBlogContext : DbContext
{
    public DbSet Blogs { get; set; }
    public DbSet Posts { get; set; }
}

The change tracking problem

Most EF applications make use of persistent ignorant POCO entities and snapshot change tracking. This means that there is no code in the entities themselves to keep track of changes or notify the context of changes.

Let’s look at the following code to illustrate the point:

using (var context = new AnotherBlogContext())
{
    var post = context.Posts
                   .Single(p => p.Title == "My First Post");

    post.Title = "My Best Post";
    context.SaveChanges();
}

In this code, a single post is queried from the database, its title is changed, and then this change is saved back to the database.

But when Title property is changed nothing special happens because it is just a simple automatic property of the C# class. There is nothing in the entity that keeps track of the fact that it has changed (such as an isDirty flag) or of the fact that the original value of this property was “My First Post”. There is also nothing in the entity that notifies the context that this change has happened.

So how does SaveChanges know that it must send an update to the database for the changed title? The answer is that is uses snapshot change tracking and DetectChanges.

Snapshot change tracking and DetectChanges

The EF context makes a snapshot of the properties of each entity when it is queried from the database. So in the example above, the context recorded in a snapshot that the Post entity had a Title property with the value “My First Post”.

When SaveChanges is called it will in turn automatically call the DetectChanges method. DetectChanges scans all entities in the context and compares the current value of each property to the original value stored in the snapshot. If the property is found to have changed, then EF knows that it must send an update for that property to the database. In the example above, the current Title value of “My Best Post” is detected as different from the original Title value of “My First Post” and an appropriate update is generated.

What else does DetectChanges do?

In reality, DetectChanges does quite a bit more than what is described above. Most of this falls under the category of “fixup” which I hope to describe in more detail in a future post. Briefly, fixup is the process of updating the references between entities and adjusting internal state and indexes appropriately.

For example, imagine that in addition to changing the Title property, the foreign key of the Post entity is also changed:

using (var context = new AnotherBlogContext())
{
    context.Blogs.Load();
    var post = context.Posts
                   .Single(p => p.Title == "My First Post");

    post.Title = "My Best Post";
    post.BlogId = 7;
    context.SaveChanges();
}

When DetectChanges is called (as part of SaveChanges) it will notice this change to the FK and do a few things:

  • It will make sure that the new FK is saved to the database, just like for any property change.
  • It will check whether or not a Blog entity with a primary key that matches the new value of the BlogId FK is being tracked by the context and, if it is, it will change the Blog navigation property to point to this entity.
  • Since the Blog type contains a Posts inverse navigation property, DetectChanges will also make sure that these collections are updated. That is, the Post will be removed from the Posts collection on the original Blog entity and added to the Posts collection of the Blog with which it is now associated.
  • It will update the state of the Blog entity, the state of its properties, and the state of any other affected entities and their properties.
  • It will update internal indexes that keep track of dangling foreign keys and conceptually (but not actually) null foreign keys as appropriate.

Note that similar things would have happened if the navigation property had been updated instead of the FK, or if the inverse navigation property had been changed.

Isn’t all this work very expensive?

If your context is tracking thousands of entities and doing a lot of work with them, then calling DetectChanges frequently can be expensive. If this is a problem for your app then read the upcoming posts to find out what to do about it.

That being said, for many applications, even when dealing with lots of entities, DetectChanges does not result in a performance bottleneck. Trying to optimize for DetectChanges when you don’t need to can be an excellent way of introducing subtle bugs into your code.

Up next: Secrets* of DetectChanges Part 2: When is DetectChanges called automatically?

*Okay, so these aren’t really secrets as such. But “secrets” sounded better than “things that EF geeks might be interested in knowing about”. And it’s shorter to tweet.

About Arthur Vickers

Developer on the Entity Framework team at Microsoft.
This entry was posted in Change Tracking, DbContext API, Entity Framework, Foreign Keys, Proxies and tagged , , , , , , , , , , . Bookmark the permalink.

9 Responses to Secrets of DetectChanges Part 1: What does DetectChanges do?

  1. Chance Wen says:

    Hello,

    Thank you for all your blog posts!

    I have a question about ChangeTracker, and I find a same question on StackOverlow:

    http://stackoverflow.com/questions/7490509/entity-framework-4-1-many-to-many-relationships-change-tracking

    Just like the comment of the question, how to user ObjectContext to track the changes of many too many relationship?

    I wonder if you could give us a anwser?

    Thank you very much!

    Chance

  2. Interesting post, thanks a lot. Does this still apply for EF5 or is this strictly an EF4.1 thing?

    • @mathewleising It applies to all versions of EF from 4.1 upwards. Actually, a lot of the content in this series applies to EF4 as well, although, as I point out at some point in the series, DetectChanges is called more often for you when using DbContext than when using ObjectContext.

  3. Also, if this is true. Do I need to call ChangeTracker.DetectChanges() as a first line of code when overriding SaveChanges because ChangeTracker.DetectChanges() is called in the base method.

  4. @mathewleising It depends what you are doing in your SaveChanges method. If you’re doing anything based on the state of entities and not using one of the other methods that already calls DetectChanges (see part 2), then yes.

  5. Ajit says:

    Hi Arthur,
    Why there is performance hit while using Context.DetectChanges(); function?? Do I need to do anything to make it faster?

    _Ajit

  6. aidnanddo says:

    Thank you for this series of articles! Helped me a lot with a Code First app !

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s