According to the reference source DbSet.Find will not access the database if an object with the same keyValues is already fetched in the DbContext:
/// Finds an entity with the given primary key values.
/// If an entity with the given primary key values exists in the context, then it is
/// returned immediately without making a request to the store.
public abstract object Find(params object[] keyValues);
FirstOrDefault, and similar functions will call IQueryable.GetEnumerator()
, which will ask the IQueryable for the interface to the Provider IQueryable.GetProvider()
and then call IQueryProvider.Execute(Expression)
to get the data defined by the Expression.
This will always access the database.
Suppose you have Schools with their Students, a simple one-to-many relationship. You also have a procedures to change Student data.
Student ChangeAddress(dbContext, int studentId, Address address);
Student ChangeSchool(dbContext, int studentId, int SchoolId);
You have this in procedures, because these procedure will check the validity of the changes, probably Eton Students are not allowed to live on Oxford Campus, and there might be Schools that only allow Students from a certain age.
You have the following code that uses these procedures:
void ChangeStudent(int studentId, Address address, int schoolId)
{
using (var dbContext = new SchoolDbContext())
{
ChangeAddress(dbContext, studentId, address);
ChangeSchool(dbContext, studentId, schoolId);
dbContext.SaveChanges();
}
}
If ChangeSchool
would use FirstOrDefault()
then you would lose changes made by ChangeAddress
. So in this case you want to access the data that might already have been fetched (and changed), in other words: use Find
.
However, sometimes you want to be able to re-fetch the database data, for instance, because others might have changed the data, or some changes you just made are invalid
int studentId = 10;
Student student = dbContext.Students.Find(studentId);
// let user change student attributes
...
bool changesAccepted = AskIfChangesOk(student);
if (!changesAccepted)
{ // Refetch the student.
// can't use Find, because that would give the changed Student
student = dbContext.Students
.Where(s => s.Id == studentId)
.FirstOrDefault();
}
// now use the refetched Student with the original data