【问题标题】:Should we always use .Find() rather than .FirstOrDefault() when we have the primary key in Entity Framework Core?当我们在 Entity Framework Core 中拥有主键时,我们是否应该始终使用 .Find() 而不是 .FirstOrDefault() ?
【发布时间】:2020-03-05 12:29:24
【问题描述】:

https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/details?view=aspnetcore-3.0

在 Microsoft 文档示例中, .FirstOrDefaultAsync()用于DetailDeleteGET.FindAsync() 用于DeleteConfirmed。 我想知道这是为什么?

【问题讨论】:

  • 但我的问题是为什么在文档示例中 .FirstOrDefaultAsync() 用于 Detail 而 Delete GET .FindAsync() 用于 DeleteConfirmed
  • @Areks EF Core 和 LINQ to Objects 没有任何关系。

标签: c# linq asp.net-core entity-framework-core


【解决方案1】:

根据reference source DbSet.Find,如果在 DbContext 中已经获取了具有相同 keyValues 的对象,则不会访问数据库:

///     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,类似的函数会调用IQueryable.GetEnumerator(),它会向IQueryable询问ProviderIQueryable.GetProvider()的接口,然后调用IQueryProvider.Execute(Expression)来获取Expression定义的数据。 这将始终访问数据库。

假设你有学校和他们的学生,一个简单的一对多关系。您还有一个更改学生数据的程序。

Student ChangeAddress(dbContext, int studentId, Address address);
Student ChangeSchool(dbContext, int studentId, int SchoolId);

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

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

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

如果 Change... 函数将使用 FirstOrDefault(),那么您将丢失其他过程所做的更改。

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

Student student = dbContext.Students.Find(10);
// let user change student attributes
...

bool changesAccepted = AskIfChangesOk();
if (!changesAccepted)
{    // Refetch the student.
     // can't use Find, because that would give the changed Student
     student = dbContext.Students.Where(s => s.Id == 10).FirstOrDefault();
}

// now use the refetched Student with the original data

【讨论】:

  • 嗨。谢谢你回答我的问题。我仍然有点想知道这是否意味着如果我使用FirstOrDefault(),它将覆盖其他程序之前所做的所有更改,这些更改在FirstOrDefault() 之前发送到数据库?在多个用户同时进行更改的情况下,何时是使用Find() 以使其仍能获得性能并确保 CRUD 仍按预期执行的最佳时间?
  • 或者换句话说,如果我使用.Find()而不是.FirstOrDefault(),会有什么不好的副作用吗?
  • 如果您使用 Find,如果您的 dbContext 对象已经获取它,您可能会获得数据库值,也可能不会。由于这种不确定性,我很少使用 Find。如果您真的想确保获得数据库值,请使用 FirstOrDefault。
【解决方案2】:

我认为是因为删除时不知道该项目是否存在,所以需要默认以防找不到。

在执行DeleteConfirmed时,您知道带有id的项目存在并且可以使用Find

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 2014-04-09
    相关资源
    最近更新 更多