When working with Entity Framework (EF), there are several methods to retrieve an entity by its primary key, such as FirstOrDefault()
, SingleOrDefault()
, and Find()
. Interestingly, Visual Studio uses the Find()
method when scaffolding controllers for Get
actions.
How Does the Find()
Method Work?
The Find()
method searches for an entity with the specified primary key values. If EF is already tracking an entity with those key values, the method returns it immediately without querying the database. If the entity is not being tracked, EF queries the database, attaches the found entity to the context, and returns it. If no entity is found, null
is returned.
Given this snippet code:
var context = new ApplicationDbContext();
var entity = context.Authors.Find(1);
var entity2 = context.Authors.Find(1);
If you check the console logs, it lightens our eyes to see there is only one query on database for the first Find method call!
The SQL query generated by the Find()
method is identical to that of the FirstOrDefault()
method. However, unlike FirstOrDefault()
, you can't use AsNoTracking()
with Find()
. This means the entity will always be tracked by EF, which can boost performance because if you call Find()
again with the same primary key, it will return the already tracked entity. So far, so good, right?
A Potential Pitfall
Consider this scenario: Between two calls to the Find()
method, you update the entity using raw SQL. In this case, the next time you call Find()
, EF will return the tracked entity, not the updated values from the database!
Enabling NoTracking for the Find()
Method
To enable NoTracking behavior for the Find()
method, you have two options:
- Enable NoTracking globally on the
DbContext
when adding it to Dependency Injection (DI). - Turn off tracking on the
DbContext
instance before calling theFind()
method.
It's important to note that when I mention EF caching the entity, this only applies to the same DbContext
instance.
Code Simple!