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。