Har*_*lse 12

根据参考源,如果 DbContext 中已获取具有相同 keyValues 的对象,则 DbSet.Find将不会访问数据库:

///     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);
Run Code Online (Sandbox Code Playgroud)

FirstOrDefault 和类似的函数将调用IQueryable.GetEnumerator(),它将向 IQueryable 询问 Provider 的接口IQueryable.GetProvider(),然后调用IQueryProvider.Execute(Expression)以获取由表达式定义的数据。这将始终访问数据库。

假设您有学校及其学生,这是一种简单的一对多关系。您还有更改学生数据的程序。

Student ChangeAddress(dbContext, int studentId, Address address);
Student ChangeSchool(dbContext, int studentId, int SchoolId);
Run Code Online (Sandbox Code Playgroud)

你在程序中有这个,因为这些程序将检查更改的有效性,可能不允许伊顿公学的学生住在牛津校园,并且可能有些学校只允许特定年龄的学生。

您有使用这些过程的以下代码:

void ChangeStudent(int studentId, Address address, int schoolId)
{
    using (var dbContext = new SchoolDbContext())
    {
        ChangeAddress(dbContext, studentId, address);
        ChangeSchool(dbContext, studentId, schoolId);
        dbContext.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果ChangeSchool使用FirstOrDefault(),那么您将丢失所做的更改ChangeAddress。因此,在这种情况下,您想要访问可能已获取(并更改)的数据,换句话说:使用Find.

但是,有时您希望能够重新获取数据库数据,例如,因为其他人可能更改了数据,或者您刚刚所做的某些更改无效

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

    
Run Code Online (Sandbox Code Playgroud)

  • 如果您使用 Find,您可能会获得数据库值,也可能不会,如果您的 dbContext 对象已经获取了它。由于这种不确定性,我很少使用“查找”。如果您确实想确保获得数据库值,请使用 FirstOrDefault。 (2认同)