Turning off Tracking for Find and FindAsync in Entity Framework
There is a lot of debate around the “best” way to retrieve a single entity using its primary key with Entity Framework.
You will see discussions about using
SingleOrDefaultAsync and their non-async counterparts.
From what I’ve seen
Find/FindAsync seems to be the most favored. It is also the one that Visual Studio will scaffold for you if you create a new controller with a
Get method that takes a primary key as a parameter.
So let’s assume
FindAsync is the “best”. The SQL it produces is the same as that of
SELECT TOP(1) [p].[ProductId], [p].[Code], [p].[Created], [p].[Description], [p].[LastModified], [p].[Name], [p].[Price], [p].[ProductCategory], [p].[SKU] FROM [Products] AS [p] WHERE [p].[ProductId] = @__id_0
If you use
SingleAsync the SQL will be will attempt to retrieve the TOP 2 rows, and throw an exception if there are more than one row is returned.
FindAsync also searches the context before generating the SQL and calling the database. If the entity is found locally, no query is made to the database. This is a distinct advantage over the others.
But the discussions around
FindAsync do not cover its lack of an
With all the others you can use the
AsNoTracking option to tell Entity Framework not to track the entity for changes. This is very useful if you are retrieving the entity to read it, and not to update it.
If you had a
Products table you could do following and generate the SQL shown above -
var product = _salesContext.Products.AsNoTracking().FirstAsync(p => p.ProductId == productId);
Very efficient, especially if have a
GET method on a controller that takes a primary key as a parameter. The context will be empty, and you won’t be updating the entity thus negating the advantages of
Turning off tracking for Find/FindAsync
But what if you want to use
Find/FindAsync and turn off tracking? There is no
Find/FindAsync extension on
EntityFrameworkQueryableExtensions, so you can’t use
For example, this will give you a compilation error -
But you can do this -
_salesContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; await _salesContext.Products.FindAsync(productId);
This will turn off tracking and then retrieve the entity.
Keep in mind, all calls to
Find/FindAsync will go to the database, even if you loaded the entity previously.
FirstAsync produce precisely the same SQL, and with tracking turned off for both it is likely that their performance will be the same, but I have not tested this. As with things performance related, you should do it yourself.
- Accessing Objects Just After they are Saved by Entity Framework
- Sending MediatR Notifications Immediately After Saving an Entity with Entity Framework Core
- An exception of type 'System.MissingMethodException' occurred in System.Collections.Concurrent.dll
- Dependency Injection of an Entity Framework Context within Program.cs Using Top Level Statements
- Requesting Data from two Data Stores in Sequence - Cache and a Database