LINQ:何时使用SingleOrDefault与FirstOrDefault()一起使用过滤条件

问题描述 投票:456回答:14

考虑IEnumerable扩展方法SingleOrDefault()FirstOrDefault()

MSDN documents that SingleOrDefault

返回序列的唯一元素,如果序列为空,则返回默认值;如果序列中有多个元素,则此方法抛出异常。

FirstOrDefault from MSDN(大概是使用OrderBy()OrderByDescending()或根本没有),

返回序列的第一个元素

考虑一些示例查询,并不总是清楚何时使用这两种方法:

var someCust = db.Customers
.SingleOrDefault(c=>c.ID == 5); //unlikely(?) to be more than one, but technically COULD BE

var bobbyCust = db.Customers
.FirstOrDefault(c=>c.FirstName == "Bobby"); //clearly could be one or many, so use First?

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or does it matter?

在LINQ查询中决定使用SingleOrDefault()FirstOrDefault()时,您遵循或建议的约定是什么?

.net linq linq-to-sql
14个回答
424
投票

每当您使用SingleOrDefault时,您都明确指出查询最多只能产生一个结果。另一方面,当使用FirstOrDefault时,查询可以返回任何数量的结果,但是您声明只需要第一个结果。

我个人发现语义非常不同,使用适当的语义,取决于预期的结果,提高了可读性。


3
投票

两者都是元素运算符,它们用于从序列中选择单个元素。但它们之间存在细微差别。如果满足多个元素,则FirstOrDefault()运算符将抛出异常,因为FirstOrDefault()不会抛出任何异常。这是一个例子。

List<int> items = new List<int>() {9,10,9};
//Returns the first element of a sequence after satisfied the condition more than one elements
int result1 = items.Where(item => item == 9).FirstOrDefault();
//Throw the exception after satisfied the condition more than one elements
int result3 = items.Where(item => item == 9).SingleOrDefault();

1
投票

正如我现在所理解的那样,如果您要查询的是保证唯一的数据(即主键等数据库约束强制执行),SingleOrDefault会很好。

或者是否有更好的方法来查询主键。

假设我的TableAcc有

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

我想查询AccountNumber 987654,我用

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);

0
投票

我向Google询问了GitHub上不同方法的用法。这是通过为每个方法运行Google搜索查询并使用查询“site:github.com file:cs ...”将查询限制到github.com域和.cs文件扩展名来完成的。

看起来First *方法比Single *方法更常用。

| Method               | Results |
|----------------------|---------|
| FirstAsync           |     315 |
| SingleAsync          |     166 |
| FirstOrDefaultAsync  |     357 |
| SingleOrDefaultAsync |     237 |
| FirstOrDefault       |   17400 |
| SingleOrDefault      |    2950 |

-1
投票

回答中遗漏了一件事......

如果存在多个结果,则没有顺序的FirstOrDefault可以根据服务器正在使用的索引策略返回不同的结果。

就个人而言,我无法忍受在代码中看到FirstOrDefault,因为据我所知,开发人员并不关心结果。尽管如此,它可以作为强制执行最新/最早的方式。我不得不纠正使用FirstOrDefault的粗心开发人员造成的很多问题。


-8
投票

我不明白为什么你使用FirstOrDefault(x=> x.ID == key),如果你使用Find(key)可以更快地检索结果。如果您使用表的主键查询,经验法则是始终使用Find(key)FirstOrDefault应该用于像(x=> x.Username == username)等谓词。

这不应该是一个downvote,因为问题的标题不是特定于DB上的linq或列表/ IEnumerable等的Linq。


533
投票

如果结果集返回0条记录:

  • SingleOrDefault返回类型的默认值(例如,int的默认值为0)
  • FirstOrDefault返回该类型的默认值

如果结果集返回1条记录:

  • SingleOrDefault返回该记录
  • FirstOrDefault返回该记录

如果结果集返回许多记录:

  • SingleOrDefault抛出一个例外
  • FirstOrDefault返回第一条记录

结论:

如果要在结果集包含许多记录时抛出异常,请使用SingleOrDefault

如果您总是想要1条记录,无论结果集包含什么,请使用FirstOrDefault


227
投票

  • 语义差异
  • 性能差异

两者之间。

语义差异:

  • FirstOrDefault返回可能多个的第一项(如果不存在,则返回默认值)。
  • SingleOrDefault假设有一个项目并返回它(如果不存在则默认)。多个项目违反合同,抛出异常。

性能差异

  • FirstOrDefault通常更快,它迭代直到找到元素,并且只有在它找不到时才需要迭代整个枚举。在许多情况下,很有可能找到一个项目。
  • SingleOrDefault需要检查是否只有一个元素,因此总是迭代整个可枚举元素。确切地说,它会迭代,直到找到第二个元素并抛出异常。但在大多数情况下,没有第二个因素。

结论

  • 如果您不关心有多少项目或者您无法负担检查唯一性(例如在非常大的集合中),请使用FirstOrDefault。当您检查将项目添加到集合时的唯一性时,在搜索这些项目时再次检查它可能太昂贵了。
  • 如果您不必过多关注性能,请使用SingleOrDefault,并希望确保读者清楚单个项目的假设并在运行时检查。

在实践中,即使在假设单个项目的情况下,也经常使用First / FirstOrDefault来提高性能。您还应该记住,Single / SingleOrDefault可以提高可读性(因为它表示单个项目的假设)和稳定性(因为它检查它)并适当地使用它。


70
投票

没有人提到在SQL中翻译的FirstOrDefault执行TOP 1记录,而SingleOrDefault执行TOP 2,因为它需要知道是否有超过1条记录。


12
投票

对于LINQ - > SQL:

的SingleOrDefault

  • 将生成类似“select * from user where userid = 1”的查询
  • 选择匹配记录,如果找到多条记录,则抛出异常
  • 如果要基于主/唯一键列提取数据,请使用此选项

FirstOrDefault

  • 将生成查询,例如“从userid = 1的用户中选择前1 *”
  • 选择第一个匹配的行
  • 如果要基于非主/唯一键列提取数据,请使用此选项

10
投票

我使用SingleOrDefault的情况下我的逻辑指示将是零或一个结果。如果有更多,这是一个错误的情况,这是有帮助的。


5
投票

SingleOrDefault:您说“最多”有一个项目与查询匹配或默认FirstOrDefault:您说的是“至少”有一个项目与查询匹配或默认

下次你需要选择大声说出来,你可能会明智地选择。 :)


4
投票

在您的情况下,我会使用以下内容:

按ID == 5选择:在这里使用SingleOrDefault是可以的,因为你期望一个[或没有]实体,如果你有多个ID为5的实体,那就有错误,绝对值得特别。

当搜索名字等于“Bobby”的人时,可能会有不止一个(很可能我会想到),所以你既不应该使用Single也不要使用First,只需选择Where-operation(如果“Bobby”返回太多)实体,用户必须优化他的搜索或选择一个返回的结果)

创建日期的顺序也应该使用Where-operation执行(不太可能只有一个实体,排序没有多大用处;)但是这意味着你想要排序所有实体 - 如果你只想要一个,请使用FirstOrDefault,如果您有多个实体,则每次都会抛出。


3
投票

在你的上一个例子中:

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or doesn't matter?

是的,它确实。如果您尝试使用SingleOrDefault()并且查询结果超过记录,您将获得和异常。唯一一次你可以安全地使用SingleOrDefault()是当你只期望1和只有1结果...

© www.soinside.com 2019 - 2024. All rights reserved.