Entity Framework 4.2

Should you use Entity Framework change-tracking proxies?

In my last post I described the different types of entity that EF supports. But which type of entity should you use?

My opinion, which may be different from others on the EF team, is this:

So when should you use change-tracking proxies? To answer that let's take a closer look at the advantages and disadvantages of change-tracking proxies over other types of POCO entities.

Advantages of change-tracking proxies

The advantages of change-tracking proxies are quite simple:

Disadvantages of change-tracking proxies

Some of the disadvantages of change-tracking proxies are:

Performance of change-tracking proxies

You may have noticed that performance can be both an advantage and a disadvantage of change-tracking proxies. This is because the performance depends on what you do with the entities.

Before getting into some specific performance scenarios its worth noting that for a lot of applications there is no noticeable difference between the performance of change-tracking proxies and entities that use snapshot change tracking. A rule of thumb is that you need to be tracking hundreds or thousands of entities at the same time in a context instance before real-world apps will see a difference. For the vast majority of apps the simpler snapshot change tracking will be just fine.

Change-tracking proxies provide the greatest performance benefit when a lot of entities are being tracked and changes are made to only a few of them. This is because snapshot change tracking needs to scan and compare all entities to detect these few changes whereas with change-tracking proxies each change is detected immediately with no scanning.

Now consider the converse—several changes being made to every tracked entity. In this case the extra cost of recording and reacting to every change when it is made starts to mount up. This “extra cost” exists because setting a simple .NET property is usually extremely fast. This means adding additional code to record state changes, fire events, and so on can soon make a difference.

Consider this simple entity type:

public class Foo
{
    public int Id { get; set; }
    public int Prop1 { get; set; }
    public int Prop2 { get; set; }
    public string Prop3 { get; set; }
    public string Prop4 { get; set; }
}

And consider performing the following update to each of 10,000 tracked entities and then calling SaveChanges:

var prop1 = foo.Prop1;
foo.Prop1 = foo.Prop2;
foo.Prop2 = prop1;
var prop3 = foo.Prop3;
foo.Prop3 = foo.Prop4;
foo.Prop4 = prop3;

On my (rather old) laptop this took approximately 6.3 seconds. Adding virtual to every property to enable change-tracking proxies and repeating resulted in a time of approximately 6.2 seconds. So not a huge performance hit for using proxies, but really not a very significant gain either.

Now consider the case where an app makes multiple changes to the same property before calling SaveChanges—perhaps because a value is being re-calculated or a counter is being incremented as application state changes. If the update above is performed three times on each entity before SaveChanges is called it still takes 6.3 seconds when using snapshot change-tracking, but now takes 6.6 seconds when using change-tracking proxies. Still not much of a difference, but the change-tracking proxies are now starting to perform worse than the snapshot change-tracking.

Finally, what happens if the app sets properties to the values that they already have, such as might happen when copying values from DTOs, forms, or similar? Consider performing this update on each of 10,000 tracked entities and then calling SaveChanges:

foo.Prop1 = foo.Prop1;

With snapshot change-tracking the whole process takes about 0.1 seconds on my laptop. This makes sense because nothing is changing and so SaveChanges writes nothing to the database. But with change-tracking proxies the same thing takes about 5.2 seconds! Why? Because change-tracking proxies mark a property as modified whenever any value is written to it. You could make a very strong case that this is broken behavior and I wouldn't argue against you. The reason it is this way is that EntityObject entities behave this way, the IPOCO interfaces where then extracted from EntityObject, and change-tracking proxies were implemented using IPOCO.

This seems very biased…

Yes, it is. The performance numbers in particular are very biased. It would be very easy to come up with many scenarios where change-tracking proxies perform better than snapshot change-tracking. Indeed, the EF team have performance micro-benchmarks that run every night and demonstrate this. But the point still remains—change-tracking proxies are not a general solution for better performance, and they shouldn't be used as such.

Summary

In summary, my advice is simple: use POCO entities optionally with virtual navigation properties for lazy loading. 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.


This page is up-to-date as of December 5th, 2011. Some things change. Some things stay the same. Use your noggin.