FirstOrDefaultAsync()和SingleOrDefaultAsync()与FindAsync() EFCore的区别

45

我们有三种不同的方法来从EFCore获取单个项,它们是FirstOrDefaultAsync()SingleOrDefaultAsync()(包括其未返回默认值版本),我们还有FindAsync()以及可能有更多与之相同的目的,例如LastOrDefaultAsync()

var findItem = await dbContext.TodoItems
    .FindAsync(request.Id)
    .ConfigureAwait(false);

var firstItem = await dbContext.TodoItems
    .FirstOrDefaultAsync(i => i.Id == request.Id)
    .ConfigureAwait(false);

var singleItem = await dbContext.TodoItems
    .SingleOrDefaultAsync(i => i.Id == request.Id)
    .ConfigureAwait(false);

我想了解它们之间的区别。到目前为止,我知道我们使用 FirstOrDefaultAsync() 条件来获取第一个满足条件的项(通常使用这个,因为我们知道有多个项目可以满足条件),另一方面,我们使用 SingleOrDefaultAsync(),因为我们知道只有一个可能的匹配,并且使用 FindAsync() 根据主键获取项。

我认为 FirstOrDefaultAsync()SingleOrDefaultAsync() 总是会与数据库交互(不确定),而 FindAsync() 是根据 Microsoft 文档的说法:

异步查找具有给定主键值的实体。如果存在具有给定主键值的实体,则立即返回该实体,而不对存储进行请求。否则,将向存储发出请求以获取具有给定主键值的实体,并将找到的该实体连接到上下文并返回。如果在上下文或存储中没有找到实体,则返回 null。

那么我的问题是,如果我们用于 FirstOrDefault()SingleOrDefault()FindAsync() 的给定条件是主键,我们是否有任何实际的差别?

我认为它们第一次使用总是会与数据库交互,但是下一次呢? 也许 EFCore 可以使用相同的上下文获取 FirstOrDefault()SingleOrDefault() 的值,就像它对 FindAsync() 做的那样,也许?


它们都是非常不同的东西,并且在所有文档中都有详细说明。关于这个问题有几个重复的,也有很多在线信息。FirstSingle总是会访问数据库,而Find则不会。此外,FirstSingle也有不同的功能。你应该根据是否只有一个记录、可能有多个记录或者你不介意它被缓存在上下文中来选择其中之一,这应该非常明显。 - TheGeneral
对正在执行的 SQL 进行数据库跟踪。一般来说,FirstOrDefault 将会执行 TOP 1,而 SingleOrDefault 将会执行 TOP 2(即稍微慢一些)。 - mjwills
1
https://github.com/aspnet/Docs/issues/4737 可以值得一读。 - mjwills
2个回答

46

这正是我一直在寻找的,但很难找到,因为它在Razor Pages的另一篇文章中。 - Diego Osornio

22

FindAsync

  • 接受主键。
  • 为了在不命中数据库的情况下,通过返回已追踪实体来优化查找具有主键的实体。
  • 无法与include方法一起使用。
  • 如果没有匹配项,则返回默认值。

FirstOrDefaultAsync

  • 接受表达式。
  • 如果需要相关实体,则比FindAsync更好的选择。
  • 如果没有匹配项,则返回默认值。

SingleOrDefaultAsync

  • 接受表达式。
  • 如果需要返回单个实体,且查询结果中只有一个匹配项,则使用此方法。
  • 如果没有匹配项,则返回默认值。
  • 接受表达式。
  • 如果未找到匹配项,则返回默认值。
  • 如果找到多个匹配项,则抛出异常。

  • 网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接