EF Migrations series
- Using Entity Framework to Create a Database
- Adding Indexes with EF Migrations
- Updating a Table with EF Migrations
- Indexes in Code-Based EF Migrations
- EF Migrations and a Merge Conflict
- Prevent EF Migrations from Creating or Changing the Database
- EF Code First Change Tracking
- EF Migrations Command Reference
- EF Code First Navigation Properties and Foreign Keys
- Update-Database MSI Custom Action
Change tracking is a central concept for every Object-Relational Mapper, including Entity Framework. When doing updates to objects the normal work flow with Entity Framework has three steps.
- Retrieve data from the database.
- Update some properties on some objects.
- Save the updates to the database.
In the third step Entity Framework has to find out what properties on what objects were changed. There are two basic strategies to accomplish this.
- Keep a copy of each object’s original state and compare it with all loaded objects when saving updates to the database.
- Have a mechanism where the object context gets notified of changes to entity objects.
A Simple Update Test
To test Entity Framework’s change tracking I’ve made a small test case.
using (TransactionScope ts = new TransactionScope()) using (CarsContext context = new CarsContext()) { Debug.WriteLine("Reading people..."); var people = context.People.ToArray(); Debug.WriteLine(string.Format("Type of people[0] is {0}", people[0].GetType().ToString())); Debug.WriteLine("Updating birth year of first person..."); people[0].BirthYear = 1965; Debug.WriteLine("Saving changes..."); context.SaveChanges(); } |
I’ve also added some debug output to the setter and getter of BirthYear
. Running the test gives a nice printout of what is happening on the debug console.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Reading people... Setting BirthYear of person 1 to 1979 Getting BirthYear of person 1 Setting BirthYear of person 2 to 2006 Getting BirthYear of person 2 Setting BirthYear of person 3 to 2009 Getting BirthYear of person 3 Type of people[0] is TestLib.Entities.Person Updating birth year of first person... Setting BirthYear of person 1 to 1965 Saving changes... Getting BirthYear of person 1 Getting BirthYear of person 1 Getting BirthYear of person 2 Getting BirthYear of person 3 Getting BirthYear of person 1 Getting BirthYear of person 1 Getting BirthYear of person 1 Getting BirthYear of person 1 Getting BirthYear of person 1 Getting BirthYear of person 1 |
The interesting part is from line 12 and below. The birth year of the changed object (person 1) multiple times. That’s fine, but shows the importance of having simple get methods. What concerns me more is line 14 and 15 where it reads the properties of the unchanged objects (person 2 and 3). This is a clear sign of change tracking strategy 1, where it searches all objects for changes on save. With just three objects it is not a problem, but if 10.000 objects have been read to memory and they are updated one by one with a separate call to SaveChanges()
after each change, there will be 10.000*10.000=100.000.000 comparisons. That can hurt performance.
The numbers are actually a real example from a program I wrote, although it was using LINQ to SQL and not Entity Framework, so I didn’t make this up.
Improving Change Tracking
To improve change tracking I want to enable Entity Framework to be notified of any changes to objects to keep it from having to compare all objects. Disabling the automatic change detection is done by a configuration on the context.
context.Configuration.AutoDetectChangesEnabled = false; |
With the automatic detecting disabled we need to introduce a way for the entity objects to notify the context of all changes to properties. It can be done by implementing the IEntityWithChangeTracker interface, which requires the entity to report all changes to a change tracker. Having to add that code to each property in each entity class would spoil much of the simplicity with code first. To avoid that the Entity Framework dynamically during runtime creates a class derived from the real entity class. The derived class contains all the plumbing code for change tracking. There are some requirements for the proxy class to be created, mainly that properties of the entity class have to be virtual
.
[Table("People")] public class Person { public virtual int PersonId { get; set; } private int birthYear; [Required] public virtual int BirthYear { get { Debug.WriteLine(string.Format( "Getting BirthYear of person {0}", PersonId)); return birthYear; } set { Debug.WriteLine(string.Format( "Setting BirthYear of person {0} to {1}", PersonId, value)); birthYear = value; } } } |
Running the code again gives a new output, that looks much better.
1 2 3 4 5 6 7 8 9 10 11 12 | Reading people... Setting BirthYear of person 1 to 1979 Setting BirthYear of person 2 to 2006 Setting BirthYear of person 3 to 2009 Type of people[0] is System.Data.Entity.DynamicProxies.Person_38F9E3ECF12561C4DEE75C2DEB2E12446626097251D3C05F7CFB746AAB9545FD Updating birth year of first person... Getting BirthYear of person 1 Setting BirthYear of person 1 to 1965 Saving changes... Getting BirthYear of person 1 Getting BirthYear of person 1 Getting BirthYear of person 1 |
There are two notable differences here. The first one is that the type of the objects in people
are no longer of type Person
but instead of a dynamic proxy type. The second difference is that SaveChanges()
no longer touches the unchanged objects.
When to Disable the Automatic Change Tracking?
I think that the automatic change tracking will be fine in most cases. Normally the working set of objects loaded in one context is so small that the comparison overhead is neglectable. It is still good to know that there are more powerful change tracking mechanisms available when needed.
In EF 5.0 this behaviour will be improved, so there should be no more need to disable automatic change tracking. EF 5.0 will use change tracking proxies where available and otherwise fall back to comparing objects. See this Stack Overflow answer for details.
More Reading
Arthur Vickers on the ADO.NET EF team has written Should you use Entity Framework change-tracking proxies? that contains some more details about the considerations involved on when to use change tracking proxies or not. He saves his advice to the end of the article.
Only consider using change-tracking proxies if you understand their issues and you have a really compelling reason to use them. This usually means only use change-tracking proxies have you have profiled your app and found snapshot change tracking to really be a bottleneck.
Arthur has also written an in depth series about change tracking that is well worth reading.
Hello Anders!
Thanks for a great article. And I have a question:
What about change tracking in entity objects that are detached from context in E.F. (4.1.)?
Currently I am using a base class where
OnPropertyChanged
method, beside risingPropertyChanged
event, it also sets a corresponding property(ex:CustomChangeStatus
) as changed. So that whenever I what to save an entity I have to check it’s status and set it as modified in context’s change tracking object. That works but is there a better way fr doing it.What would you recommend on such(detached entity) case?
Thanks!
I haven’t used detached objects myself with EF Code First yet, but it looks like it’s a weak spot of EF Code First. If I can avoid it, I prefer to never detach objects. If the object has been sent to another tier (or in the case of MVC a fresh object was created by the model binder) I often read up the entity from the database, assigns values property-by-property to the entity and then saves it back. It’s a bit awkward, but it works and I don’t have to mess with the change tracking.