Way back in 2012, I added a feature request to EF6 to allow us to define a context that will never track entities that it retrieves from the database.
(Support Read-Only Context or DbSet)
This is instead of having to add AsNoTracking to all of your queries if you have a DbContext you are using for read-only data. Or more importantly, if your DbContext is being used for data that’s going to be disconnected and therefore never tracked post-query. That means a Web API or service or a controller. Tracking can be expensive if you are retrieving a lot of data and have no plans to update it. And having to remember to add AsNoTracking to every query is a PIA.
I just discovered that this is possible with EF Core and I think it was even in the EF Core 1.0 release!
There is a ChangeTracker property called QueryTrackingBehavior that takes a QueryTrackingBehavior enum whose options are NoTracking and TrackAll.
There are plenty of places to use it, but the one I’m excited about is to place it directly in the constructor of a DbContext to make that context default to never track any entities.
public class BookContext : DbContext { public BooksReadOnlyContext() { ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } public DbSet<Book> Books {get;set;} etc ...
A quick test where I retrieved some books and the inspected the ChangeTracker.Entries returned 0, to show that this was doing what I’ve been dreaming of for over 5 years! Thanks EF team!
Console.WriteLine( $"Tracked Entities: {context.ChangeTracker.Entries().Count()}");
Another point to be aware of is that just as you have always been able to use the DbSet’s AsNoTracking method to turn off tracking for a particular query, you can now use AsTracking to turn on tracking for a particular query.
Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!
Awesome Julie, can’t wait to use this 🙂
This is awesome
Hi Julie,
Do you think there is any performance advantage in doing this with EF 6 (in a Web API for immediate JSON serialization):
var productsForCategory = (from p in context.Products.AsNoTracking()
where p.Category.CategoryName == selectedCategory
select p).ToList();
Over this
var productsForCategory = (from p in context.Products
where p.Category.CategoryName == selectedCategory
select p).ToList();
Hi Julie,
Reading your post I remembered myself learning EF basics a while ago and thinking about entities tracking for simple blog.
For example if we have 3 tables in our database
BlogPosts
PostComments
CommtoPostMapping
Is it make sense to create two DbSets one for getting posts (read-only) and another one for comments (read-write), or it simplier to disable tracking of queary when getting posts from database just to display them on the website?
Thx,
Sergey Sypalo | sypalo.com
sounds CQRS-y to me. I recommend reading my new MSDN Mag column and then considering having a no tracking context for the reads and then a separate context that allows you to write comments. http://msdn.microsoft.com/magazine/mt788619
Nice, your point about tracking being expensive hadn’t even occurred to me! Thanks Julie.
Am I wrong? I think that was always possible in EF6 using the following code in the ctor of the context:
Configuration.AutoDetectChangesEnabled = false
no that’s different. That just determines if EF goes to update the state in the change tracker immediately when you make a change to a tracked object.
Ok. I was wrong all the time since now.
Thanks for clarifying!
oh! Glad to help clear that up. 🙂
Thanks July! I watch you whenever you’ve got new stuff on pluralsight, vs toolbox, etc.
In EF.Core 1.1, is there better way than below to use both FindAsync and AsNoTracking? I cannot seem to combine .AsNoTracking().FindAsync(id).
“`
var previousBehavior = _context.ChangeTracker.QueryTrackingBehavior;
_context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var student = await _context.Students.FindAsync(id);
_context.ChangeTracker.QueryTrackingBehavior = previousBehavior;
if (student == null)
{
return NotFound();
}
“`
Any possibility to retrofit this to EF 6.x ? I have forked EF locally for some lazy loading magic, so I’m able to patch my EF. How hard would it be to add this somehow to EF 6? (Is there a single point where DB-Sets are created where I could automatically set its MergeOption to notrack IIF the ctx has been set to NoTrack? This ctx-wide flag I’d add myself).
No clue. No ideas. I remember trying all kinds of tricks years ago to make a blanket “no tracking” context. Sorry. 🙁