Does getting entities with AsNoTracking() disable the automatic call to DetectChanges()?
Solution 1
will it also cause disabling the automatic call to DetectChanges()
No it won't. But you must realize that AsNoTracking
and DetectChanges
have nothing to do with each other (apart from being part of EF). Objects fetched with AsNoTracking
will never be change detected anyway, whether AutoDetectChanges is enabled or not. Besides, AsNoTracking
works on a DbSet
level, AutoDetectChangesEnabled
on the context level. It would be bad to have a DbSet
method affect the whole context.
or that [setting
AutoDetectChangesEnabled
] has to be done explicitly
Well, you probably just shouldn't disable AutoDetectChanges. If you do it you must know what you do.
what impact(in terms of performance) does it have if both of the action is performed
As said, they are not related. They can both improve performance in their own way.
AsNoTracking
is great if you want to fetch read-only data. It has no side effects (as in: its effect is clear)Setting
AutoDetectChangesEnabled = false
stops automatic calls ofDetectChanges
(which can be numerous), but it has side effects you need to be aware of. From Lerman & Miller's book DbContext:Working out when DetectChanges needs to be called isn’t as trivial as it may appear. The Entity Framework team strongly recommends that you only swap to manually calling DetectChanges if you are experiencing performance issues. It’s also recommended to only opt out of automatic DetectChanges for poorly performing sections of code and to reenable it once the section in question has finished executing.
Solution 2
We have found that setting AutoDetectChangesEnabled = false
can have substantial (i.e. factor of 10) performance impacts.
Background: Our system is composed entirely of EF model objects that use change detecting proxies. That is, all of our DB fields and relational properties are declared as virtual. We also have a relatively deeply structured object model. That is object A contains a set of object B, which in turn contain a set of Object C, etc. We have observed that instantiating a non-trivial (> 100) number of these objects via a EF/LINQ query is expensive. For example, in one case instantiating 250 objects required about 2 seconds. We also observed that instantiating the same structure, but using anonymous objects instead required about 25 ms. Lastly, we observed that if we set AutoDetectChangesEnabled = false
, that we could use the query instantiating EF model objects and materialization again was about 25 ms.
So, at least for us, there were huge gains to be made by setting it false. We use a Unit Of Work pattern, and we explicitly indicate whether the Unit of Work is read-only or not. For a read-only unit of work, setting AutoDetectChangesEnabled = false
is perfectly safe, since there never will be any changes. In fact, we added this change to our system two years after our initial release (so there were many, many pre-existing unit of works in the code) and the change did not break anything at all, and significantly improved performance.
We also experimented with AsNoTracking()
and found that it gave us essentially no performance increase at all. As I understand it, a query with AsNoTracking()
means that the objects will not be placed into the identity map, and this will force EF to re-fetch the object from disk if it is referenced more than once within the context (e.g. in different queries). So there is some potential downside to AsNoTracking()
.
Implementation Details:
- We have a subclass of DBContext which provides much of the infrastructure for our unit of work
- Our unit of work is basically a light weight wrapper around the context
- When a unit of work is allocated (typically in a using block) it receives one of these contexts via injection (we use Castle/Windsor)
- During initialization the unit of work calls a method on the context to which sets AutoDetectChangesEnabled to false
- Currently, we do this all the time because we always use change detecting proxies and they do not require AutoDetectChangesEnabled
- Previously we only did it for 'read-only' units of work, since if nothing is modified in a UoW there is no need to detect changes (when we allocated a unit of work we explicitly indicate whether it is read-only or not) -
Related videos on Youtube
Sayan Pal
Also involved in Open Source works. Buy me a coffee if you want :)
Updated on September 13, 2020Comments
-
Sayan Pal almost 4 years
I've come to know this concept of
AsNoTracking()
,DetectChanges()
, andAutoDetectChangesEnabled
very recently. I understand that when fetching records from the database via Entity Framework withAsNoTracking()
used, then Entity Framework does not track any changes on those records and updating any property of the fetched record will fail in that case.My question is if records are fetched in that manner, will it also cause disabling the automatic call to DetectChanges() or does that have to be done explicitly by setting:
Context.Configuration.AutoDetectChangesEnabled = false;
Also kindly let me know what impact (in terms of performance) does it have if both of the actions are performed while fetching the data strictly for read only purposes:
Context.Configuration.AutoDetectChangesEnabled = false; Context.Set<T>().AsNoTracking();
-
Sayan Pal over 10 yearsThanks Gert for elucidating the concept. Would it be safe to state/assume that when a set of records is fetched with AsNoTracking() entity framework does not call DetectChanges() for that particular set of records?
-
YodasMyDad almost 9 yearsthanks for this awesome answer, any chance of updating the answer to show how you are enabling and disabling AutoDetectChangesEnabled in your UoW? We also have a UoW and I was trying to figure out how I could achieve exactly what you have described.
-
Doug over 8 yearsIt's also important to emphasize that for entities fetched with AsNoTracking(), even manual calls to DetectChanges() will not detect changes in these objects. They are totally disconnected from the context.
-
Terry Coatta over 8 yearsActually, we turn off change tracking in our class that wraps
DbContext
. We do this because we use change tracking proxies everywhere and so change tracking is never required (its basically built-in to the generated proxy classes). In theDBContext
its just a single line of code:this.Configuration.AutoDetectChangesEnabled = false;
-
Terry Coatta over 8 yearsWith respect to 'working out when DetectChanges need to be called' if you are using change tracking proxies, then you actually don't need to work this out because the change tracking proxies handle change tracking at a fine-grain level (which does not have the same performance impact that DetectChanges does). Proxies have overhead of their own so it depends on the nature of your application, but we use change tracking proxies combined with AutoDetectChanges = false, and it has worked out well.
-
Saber over 7 yearsAsNoTracking can sometimes have a negative effect: stackoverflow.com/questions/9259480/…