LINQ:什么时候使用SingleOrDefault vs. FirstOrDefault()过滤条件

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

# EYZ1:

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

FirstOrDefault从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?

问题

当决定使用SingleOrDefault()FirstOrDefault()在你的LINQ查询你遵循或建议什么惯例 ?

379600 次浏览

  • 语义上的差异
  • 性能差异

两者之间。

语义的区别:

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

性能差异

  • FirstOrDefault通常更快,它迭代直到找到元素,只有在找不到元素时才迭代整个枚举对象。在许多情况下,有很大的可能性找到一个项目。

  • SingleOrDefault需要检查是否只有一个元素,因此总是迭代整个枚举对象。准确地说,它迭代直到找到第二个元素并抛出异常。但在大多数情况下,没有第二个元素。

结论

  • 如果你不关心有多少项,你可以使用FirstOrDefault(例如在一个非常大的集合中)。当您在将项添加到集合时检查唯一性时,在搜索这些项时再次检查它可能代价太大。

  • 如果您不需要太关心性能,并且希望确保单个项的假设对读者来说是清楚的,并在运行时进行检查,则使用SingleOrDefault

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

无论何时使用SingleOrDefault,您都清楚地声明查询结果最多应该是。另一方面,当使用FirstOrDefault时,查询可以返回任意数量的结果,但您声明只想要第一个结果。

我个人认为语义非常不同,根据预期结果使用适当的语义可以提高可读性。

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

在上一个例子中:

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

是的。如果您尝试使用SingleOrDefault()和查询结果在多个记录,您将得到一个异常。你唯一可以安全地使用SingleOrDefault()的时候是当你只期待一个结果的时候……

在你的情况下,我会使用以下方法:

select by ID==5:在这里使用SingleOrDefault是可以的,因为你希望有一个[或没有]实体,如果你有多个ID为5的实体,那就有问题了,肯定值得异常处理。

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

按创建日期的顺序也应该执行一个where操作(不太可能只有一个实体,排序不会有太大的用处;)这意味着你想要所有的实体排序-如果你想要一个,使用FirstOrDefault, Single会抛出每次如果你有多个实体。

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

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

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

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

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

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

如果你的结果集返回很多记录:

  • SingleOrDefault抛出异常
  • FirstOrDefault返回第一条记录

结论:

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

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

没有人提到SQL中转换的FirstOrDefault执行TOP 1记录,而SingleOrDefault执行TOP 2记录,因为它需要知道是否有多个记录。

在回答中漏掉了一件事....

如果有多个结果,没有排序by的FirstOrDefault可以根据服务器碰巧使用的索引策略返回不同的结果。

就我个人而言,我无法忍受在代码中看到FirstOrDefault,因为对我来说,这表明开发人员不关心结果。通过命令,它可以作为一种强制执行最新/最早的方式。我不得不纠正许多由于粗心的开发人员使用FirstOrDefault而引起的问题。

所以,据我现在的理解,SingleOrDefault将是很好的,如果你正在查询数据,保证是唯一的,即强制的DB约束,如主键。

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

假设我的TableAcc有

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

我想查询一个AccountNumber 987654,我使用

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

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

这个问题的标题并不专门针对DB上的linq或linq到List/IEnumerable等,所以不应该被否决。

两者都是元素操作符,用于从序列中选择单个元素。但是它们之间有一个微小的区别。SingleOrDefault()操作符将在多个元素满足条件时抛出异常,而as 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();

对于LINQ ->SQL:

SingleOrDefault

  • 将生成查询,如“select TOP 2 * from users where userid = 1”;
  • 选择匹配的记录,如果发现多个记录则抛出异常
  • 如果基于主键/唯一键列获取数据,则使用

FirstOrDefault

  • 将生成查询,如“select top 1 * from users where userid = 1”;
  • 选择第一个匹配的行
  • 如果基于非主键/唯一键列获取数据,则使用

我查询了谷歌在GitHub上不同方法的使用情况。这是通过对每个方法运行谷歌搜索查询,并通过使用查询“site:github.com file:cs…”来限制查询到github.com域和.cs文件扩展名。

似乎第一个*方法比单一*方法更常用。

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

在我看来,FirstOrDefault被滥用了很多。在大多数情况下,当您过滤数据时,您要么期望返回匹配逻辑条件的元素集合,要么通过其惟一标识符返回单个惟一元素——例如用户、图书、帖子等……这就是为什么我们甚至可以说FirstOrDefault()是一种代码气味,不是因为它有什么问题,而是因为它被使用得太频繁了。这篇博文详细探讨了这个主题。在我看来,大多数时候SingleOrDefault()是一个更好的选择,所以要注意这个错误,并确保你使用了最合适的方法,清楚地代表了你的合同和期望。

从本质上讲,这给了你某种验证来清理你的数据,如果你选择其中一个,它会同时给出数据,但SingleOrDefault会让你意识到,当你期望的数据应该只有1个结果,并吐出更多的1,那么你需要看看为什么你的存储过程或查询导致这样,重复的项目在查询中从来都不好。