LINQ:何时使用SingleOrDefault与FirstOrDefault()与过滤标准

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

MSDN文档SingleOrDefault

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

而来自MSDN的FirstOrDefault (大概在使用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()时, 你遵循什么约定

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

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

如果结果集返回0个logging:

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

如果结果集返回1logging:

  • SingleOrDefault返回该logging
  • FirstOrDefault返回该logging

如果您的结果集返回许多logging:

  • SingleOrDefault引发exception
  • FirstOrDefault返回第一条logging

结论:

如果您想要在结果集包含许多logging时抛出exception,请使用SingleOrDefault

如果无论结果集包含什么,总是需要1条logging,请使用FirstOrDefault

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

两者之间。

语义差异:

  • FirstOrDefault返回可能为多个的第一项(如果不存在,则返回默认值)。
  • SingleOrDefault假定有一个项目并返回它(或者如果不存在则返回默认值)。 多个项目违反合同,将引发exception。

性能差异

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

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

结论

  • 如果您不关心有多less项目或者无法检查唯一性(如在一个非常大的集合中),则使用FirstOrDefault 。 当您检查将项目添加到集合的唯一性时,在search这些项目时再次检查它可能太昂贵。

  • 使用SingleOrDefault如果您不必关心性能太多,并且希望确保单个项目的假设对读者是清楚的并且在运行时被检查。

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

没有人提到过,在SQL中FirstOrDefault翻译成TOP 1logging,而SingleOrDefault做TOP 2,因为它需要知道是否有超过1条logging。

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

SingleOrDefault:你说“最多”有一个项目匹配查询或默认FirstOrDefault:你说有至less一个项目匹配查询或默认

在下次需要select时大声说出来,你可能会明智地select。 🙂

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

selectID == 5:在这里使用SingleOrDefault是可以的,因为你期望一个[或者没有]实体,如果你有多于一个的ID为5的实体,就会出现错误,肯定是值得的。

当search名字等于“Bobby”的人时,可以有多个(很可能我会想),所以你既不应该使用Single也不要First,只要selectWhere操作(如果“Bobby”返回太多实体,用户必须改进他的search或select返回的结果之一)

创builddate的顺序也应该用一个Where操作来执行(不太可能只有一个实体,sorting不会有太大的用处),但是这意味着你想要所有的实体sorting – 如果你只需要一个,使用FirstOrDefault,如果你有一个以上的实体,每次都会抛出。

在你最后的例子中:

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

是的,它确实。 如果您尝试使用SingleOrDefault()并且查询结果超过了logging,您将得到和exception。 唯一一次你可以安全地使用SingleOrDefault()是当你期待只有1,只有1结果…

两者都是元素操作符,它们用于从序列中select单个元素。 但他们之间有一个小的区别。 SingleOrDefault()运算符会抛出一个exception,如果不止一个元素满足的条件,FirstOrDefault()不会抛出任何exception。 这里是例子。

 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(); 

所以,现在我明白了, SingleOrDefault将是好的,如果你正在查询的数据是保证是唯一的,如数据库约束如主键强制执行。

还是有更好的方式来查询主键。

假设我的TableAcc有

 AccountNumber - Primary Key, integer AccountName AccountOpenedDate AccountIsActive etc. 

我想查询一个AccountNumber 987654 ,我用

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

有一件事是在答复中遗漏的….

如果有多个结果,FirstOrDefault没有sorting可以根据服务器曾经使用过哪个索引策略返回不同的结果。

就我个人而言,我不能忍受在代码中看到FirstOrDefault,因为对我而言,开发人员并不关心结果。 通过一个顺序,虽然它可以作为执行最新/最早的方式有用。 我必须纠正使用FirstOrDefault的粗心的开发人员造成的很多问题。

对于LINQ – > SQL:

的SingleOrDefault

  • 会生成查询,如“select * from userid where userid = 1”
  • select所有匹配的行
  • 如果find多个logging,则抛出exception
  • 如果您正在根据主要/唯一键列获取数据,请使用此选项

FirstOrDefault

  • 会产生类似于“userid = 1的用户select顶部1 *”的查询
  • select第一个匹配的行
  • 根据非主要/唯一键列提取数据时使用

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

这个问题不值得赞同,因为问题的标题并不是特定于DB或Linq到List / IEnumerable等的LINQ。