LINQ : 필터링 기준으로 SingleOrDefault와 FirstOrDefault ()를 함께 사용하는 경우


505

IEnumerable 확장 방법을 고려 SingleOrDefault()하고FirstOrDefault()

다음과 같은 MSDN 문서SingleOrDefault :

시퀀스의 유일한 요소 또는 시퀀스가 ​​비어있는 경우 기본값을 반환합니다. 이 메소드는 시퀀스에 둘 이상의 요소가있는 경우 예외를 발생시킵니다.

반면 FirstOrDefaultMSDN에서 (아마도 OrderBy()or를 사용 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() 합니까?

답변:


465

을 사용할 때마다 SingleOrDefault쿼리 결과가 최대 하나의 결과로 나타나야한다는 것이 분명합니다 . 반면에을 FirstOrDefault사용하면 쿼리는 많은 양의 결과를 반환 할 수 있지만 첫 번째 결과 만 원한다고 명시합니다.

나는 개인적으로 의미가 매우 다르다는 것을 알고 예상되는 결과에 따라 적절한 것을 사용하면 가독성이 향상됩니다.


164
매우 중요한 차이점은 둘 이상의 요소가있는 시퀀스에서 SingleOrDefault를 사용하면 예외가 발생한다는 것입니다.
Kamran Bigdely

17
@kami 예외가 발생하지 않으면 FirstOrDefault와 정확히 같습니다. 단, SingleOrDefault는 예외입니다. 좋은 점을 기르고 차이의 관에 못을 박습니다.
Fabio S.

17
퍼포먼스 측면에서 FirstOrDefault는 9,000,000 요소의 List <MyClass>를 사용하여 SingleOrDefault보다 약 10 배 더 빠르게 작동하고 클래스에는 2 개의 정수가 있고 Func에는 2 개의 정수에 대한 검색이 포함되어 있다고 말해야합니다. 루프에서 200 번 검색하는 데는 var v = list에서 22 초가 걸렸습니다 .SingleOrDefault (x => x.Id1 == i && x.Id2 == i); 그리고 var v = list.FirstOrDefault (x => x.Id1 == i && x.Id2 == i); 약 3 초
Chen

6
@BitsandBytesHandyman 시퀀스에 둘 이상의 항목이 포함 된 경우 SignleOrDefault에서 예외가 발생하지 않으면 FirstOrDefault와 똑같이 동작하지 않습니다. FirstOrDefault는 첫 번째 항목을 반환하거나 시퀀스가 ​​비어 있으면 null을 반환합니다. SingleOrDefault는 유일한 항목을 반환하거나, 시퀀스가 ​​비어 있거나 예외가 전혀없는 경우 둘 이상의 항목을 포함하는 경우 null을 반환해야합니다.
Thanasis Ioannidis

2
@RSW 예, 알고 있습니다. 내 의견을주의 깊게 읽으면서 SingleOrDefault가 수행하는 것이 아니라 수행 해야하는 것을 말하고있었습니다. 그러나 물론 그것이해야 할 일은 매우 주관적입니다. 저에게 "SomethingOrDefault"패턴은 다음을 의미합니다. "Something"의 값을 얻습니다. "Something"이 값을 리턴하지 못하면 기본값을 리턴하십시오. "Something"에서 예외가 발생하는 경우에도 기본값이 반환되어야 함을 의미합니다. 따라서 Single이 예외를 throw하는 경우 SingleOrDefault는 내 관점에서 기본값을 반환해야합니다.
Thanasis Ioannidis

585

결과 세트가 0 개의 레코드를 리턴하는 경우 :

  • SingleOrDefault 유형의 기본값을 반환합니다 (예 : int의 기본값은 0).
  • FirstOrDefault 유형의 기본값을 반환합니다

결과 세트가 1 개의 레코드를 리턴하는 경우 :

  • SingleOrDefault 그 레코드를 반환
  • FirstOrDefault 그 레코드를 반환

결과 세트가 많은 레코드를 리턴하는 경우 :

  • SingleOrDefault 예외를 던지다
  • FirstOrDefault 첫 번째 레코드를 반환

결론:

결과 세트에 많은 레코드가 포함 된 경우 예외가 발생하도록하려면을 사용하십시오 SingleOrDefault.

결과 집합에 포함 된 내용에 관계없이 항상 1 개의 레코드를 원할 경우 FirstOrDefault


6
실제로 예외를 원하지 않는 경우가 많으므로 FirstOrDefault가 대부분 선호됩니다. 나는 종종 imo가 아닌 경우가 존재한다는 것을 알고 있습니다.
MikeKulls

FirstOrDefault첫 번째 레코드가 반환되면 새 레코드 (마지막) / 오래된 레코드 (첫 번째)를 의미합니까?
Duk

@Duk, 레코드 정렬 방법에 따라 다릅니다. FirstOrDefault를 호출하기 전에 OrderBy () 또는 OrderByDescending () 등을 사용할 수 있습니다. OP의 코드 예제를 참조하십시오.
Gan

5
이 답변도 좋아합니다. 특히 예외 발생이 아닌 다른 곳에서 제대로 처리하기 위해 예외가 실제로 발생하는 경우가 있습니다. 예외를 원할 때 이것을 명확하게 말하고 전체 시스템을보다 견고하게 만드는 다른 사람들이 처리하도록 강요합니다.
Francis Rodgers

쉽게 이해할 수 있도록 명확하게 명시되어 있습니다.
Nirav Vasoya

243

있다

  • 의미상의 차이
  • 성능 차이

둘 사이.

의미 차이 :

  • FirstOrDefault 잠재적으로 여러 항목의 첫 번째 항목을 반환합니다 (없는 경우 기본값).
  • SingleOrDefault단일 항목이 있다고 가정하고이를 반환합니다 (없는 경우 기본값). 여러 항목이 계약 위반이며 예외가 발생합니다.

성능 차이

  • FirstOrDefault일반적으로 더 빠르며 요소를 찾을 때까지 반복되며 찾을 수 없을 때 열거 가능한 전체를 반복해야합니다. 대부분의 경우 항목을 찾을 가능성이 높습니다.

  • SingleOrDefault요소가 하나만 있는지 확인해야하므로 항상 전체 열거 가능을 반복합니다. 정확히 말하면 두 번째 요소를 찾을 때까지 반복되고 예외가 발생합니다. 그러나 대부분의 경우 두 번째 요소는 없습니다.

결론

  • 사용 FirstOrDefault당신이 얼마나 많은 항목을 상관하지 않는 경우 또는 당신이 확인 고유성을 감당할 수없는 경우 (매우 큰 컬렉션의 예). 컬렉션에 항목을 추가 할 때 고유성을 확인하면 해당 항목을 검색 할 때 다시 확인하기에는 너무 비쌀 수 있습니다.

  • 사용 SingleOrDefault당신이 너무 많이 성능에 관심을 가지고 있는지 단일 항목의 가정이 독자에게 명확하고 런타임에 선택되어 있는지 확인하지 않으려면.

실제로 성능을 향상시키기 위해 단일 항목을 가정하는 경우에도 First/를 FirstOrDefault자주 사용 합니다. 당신은 아직도 기억한다 Single/ SingleOrDefault(그것을 확인하기 때문에)과 안정성 (는 단일 항목의 가정 상태 때문에) 가독성을 개선하고 적절하게 사용할 수 있습니다.


16
+1 "또는 고유성을 확인할 여유가없는 경우 (예 : 매우 큰 컬렉션)." . 나는 이것을 찾고 있었다. 또한 쿼리 할 때 대신 삽입하거나 디자인 할 때 고유성을 강화 합니다!
Nawaz

SingleOrDefaultLinq to Objects를 사용할 때 많은 객체를 반복 한다고 상상할 수 있지만 SingleOrDefaultLinq가 데이터베이스와 대화하는 경우 최대 2 개의 항목을 반복 할 필요는 없습니까? 그냥 궁금해 ..
Memet Olsen 2012

3
@memetolsen LINQ to SQL과 함께 두 코드 스핏을 고려하십시오-FirstOrDefault는 Top 1을 사용합니다. SingleOrDefault는 Top 2를 사용합니다.
Jim Wooley

@ JimWooley '열거 가능하다'라는 단어를 오해 한 것 같습니다. Stefan이 C #을 의미한다고 생각했습니다 Enumerable.
Memet Olsen 2016

1
@ memetolsen 원래 답변의 관점에서 정확합니다. 귀하의 의견은 데이터베이스를 언급하고 있으므로 제공자로부터 발생하는 일을 제안하고있었습니다. .Net 코드는 2 개 이상의 값만 반복하지만 데이터베이스는 기준을 충족하는 두 번째 코드에 도달 할 때까지 필요한만큼의 레코드를 방문합니다.
Jim Wooley

76

SQL로 변환 된 FirstOrDefault는 TOP 1 레코드를 수행하고 SingleOrDefault는 TOP 2를 수행한다고 언급 한 사람은 없습니다. 레코드가 둘 이상 있다는 것을 알아야하기 때문입니다.


3
LinqPad와 VS를 통해 SingleOrDefault를 실행했을 때 SELECT TOP 2를 얻지 못했습니다. FirstOrDefault를 사용하면 SELECT TOP 1을 얻을 수 있었지만 SELECT TOP 2를 얻지 못한다고 말할 수 있습니다.
Jamie R Rytlewski

안녕하세요, linqpad에서도 시도했지만 SQL 쿼리는 모든 행을 완전히 가져 오기 때문에 두려워했습니다. 어떻게 이런 일이 일어날 수 있는지 잘 모르겠습니다.
AnyOne

1
이는 전적으로 사용 된 LINQ 공급자에 따라 다릅니다. 예를 들어 LINQ to SQL 및 LINQ to Entities는 다른 방식으로 SQL로 변환 될 수 있습니다. 난 그냥 IQ MySQL의 공급자와 LINQPad을 시도하고, FirstOrDefault()추가 LIMIT 0,1동안 SingleOrDefault()아무것도 추가하지 않습니다.
Lucas

1
EF 코어 2.1 (2) SELECT TOP에 SELECT TOP (1)에 SingleOrDefault FirstOrDefault 변환
camainc

19

LINQ-> SQL의 경우 :

단일 또는 기본값

  • "userid = 1 인 사용자의 * 선택"과 같은 쿼리를 생성합니다.
  • 일치하는 레코드 선택, 둘 이상의 레코드가 발견되면 예외가 발생합니다.
  • 기본 / 고유 키 열을 기반으로 데이터를 가져 오는 경우 사용

FirstOrDefault

  • "userid = 1 인 사용자의 상위 1 * 선택"과 같은 쿼리를 생성합니다.
  • 첫 번째로 일치하는 행을 선택하십시오.
  • 기본이 아닌 고유 키 열을 기반으로 데이터를 가져 오는 경우 사용

SingleOrDefault에서 "일치하는 모든 행 선택"을 제거해야한다고 생각합니다.
Saim Abdullah

10

SingleOrDefault내 논리에 따라 0 또는 하나의 결과가 표시되는 상황에서 사용 합니다. 더 많은 것이 있으면 오류 상황이며 도움이됩니다.


3
종종 SingleOrDefault ()가 결과 집합에 올바른 필터링을 적용하지 않았거나 기본 데이터에 중복 문제가있는 경우를 강조합니다. First () 메소드에 대해 Single () 및 SingleOrDefault ()를 사용하는 경우가 종종 있습니다.
TimS

열거 가능하지만 데이터베이스 (예 : SQL Server)와 대화 할 때 상위 2 호출을 수행하고 인덱스를 올바르게 설정하면 LINQ to Objects에서 Single () 및 SingleOrDefault ()에 성능 영향이 있습니다. 호출 비용이 비싸지 않아야하며 First () 또는 FirstOrDefault ()를 호출 할 때 잘못된 복제를 수행하여 다른 데이터 문제를 발생시키는 대신 빠르게 실패하고 데이터 문제를 찾으십시오.
heartlandcoder

5

SingleOrDefault : 쿼리 또는 기본값과 일치하는 항목이 "대부분"있다고 말하거나 FirstOrDefault : 쿼리 또는 기본값과 일치하는 항목이 "적어도"있다고 말하고 있습니다.

다음에 더 크게 선택해야하며 현명하게 선택해야한다고 가정하십시오. :)


5
실제로 결과가 없으면 FirstOrDefault. More correctly: FirstOrDefault를 완벽하게 사용할 수 있습니다 .` = 임의의 수의 결과이지만 첫 번째 결과 만 신경 쓰면 결과가 없을 수도 있습니다. SingleOrDefault= 1 개 또는 0 개의 결과가 있습니다. 더 많으면 어딘가에 오류가 있음을 의미합니다. First= 하나 이상의 결과가 있으며 원하는 결과입니다. Single= 정확히 1 개의 결과가 더 이상없고 더 이상 필요하지 않습니다.
Davy8

4

귀하의 경우 다음을 사용합니다.

ID == 5로 선택 : 여기에서 SingleOrDefault를 사용하는 것이 좋습니다. 하나 또는 하나의 엔터티를 기대할 수 있기 때문에 ID 5의 엔터티가 둘 이상인 경우 잘못된 예외가있을 수 있습니다.

이름이 "Bobby"인 사람을 검색 할 때 둘 이상이있을 수 있으므로 (아마도 생각할 것입니다.) Single 또는 First를 사용하지 말고 Where-operation으로 선택하십시오 ( "Bobby"가 너무 많은 경우 엔터티의 경우 사용자가 검색을 세분화하거나 반환 된 결과 중 하나를 선택해야합니다)

생성 날짜 별 순서도 Where-operation으로 수행해야합니다 (하나의 엔터티 만 가질 수는 없지만 정렬은별로 사용되지 않습니다.) 그러나 이것은 모든 엔터티를 정렬하려는 것을 의미합니다. 하나만 원하면 FirstOrDefault, 하나 이상의 엔터티가 있으면 싱글은 매번 던질 것입니다.


3
동의하지 않습니다. 데이터베이스 ID가 기본 키인 경우 데이터베이스는 이미 고유성을 강화하고있는 것입니다. 데이터베이스가 모든 쿼리에서 작업을 수행하는지 확인하기 위해 CPU 사이클을 낭비하는 것은 어리석은 일입니다.
존 헨켈

4

둘 다 요소 연산자이며 시퀀스에서 단일 요소를 선택하는 데 사용됩니다. 그러나 그들 사이에는 약간의 차이가 있습니다. SingleOrDefault () 연산자는 FirstOrDefault ()가 동일한 예외를 throw하지 않는 조건을 두 개 이상의 요소가 충족하면 예외를 throw합니다. 다음은 예입니다.

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

2
"그들 사이에 사소한 차이가 있습니다"-그게 중요합니다!
Nikhil Vartak

3

마지막 예에서 :

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

그렇습니다. 사용하려고 SingleOrDefault()하면 쿼리 결과가 레코드보다 많은 경우 예외가 발생합니다. 당신이 안전하게 사용할 수있는 유일한 시간 SingleOrDefault()은 단 하나의 결과 만 기대할 때입니다 ...


맞습니다. 결과가 0이면 예외도 발생합니다.
Dennis Rongo

1

내가 지금 이해 SingleOrDefault하는 것처럼, 기본 키와 같은 DB 제약 조건에 의해 고유 한 것으로 보장되는 데이터를 쿼리하는 경우 좋습니다.

또는 기본 키를 쿼리하는 더 좋은 방법이 있습니까?

내 TableAcc가 있다고 가정

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

그리고 나는에 대한 조회 할 AccountNumber 987654, 내가 사용

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

1

제 생각 FirstOrDefault에는 많이 남용되고 있습니다. 대부분의 경우 데이터를 필터링하는 경우 논리적 조건과 일치하는 요소 컬렉션이나 고유 식별자 (예 : 사용자, 서적, 게시물 등)로 고유 한 단일 요소를 가져올 수 있습니다. 왜 우리는 FirstOrDefault()코드 냄새가 나쁘다는 것이 아니라 너무 자주 사용되기 때문에 코드 냄새 라고 말할 수 있습니다. 이 블로그 게시물 에서는 주제를 자세히 살펴 봅니다. 대부분의 경우 IMO SingleOrDefault()가 훨씬 더 나은 대안이므로이 실수를 조심하고 계약과 기대를 명확하게 나타내는 가장 적절한 방법을 사용해야합니다.


-1

응답에서 빠진 한 가지 ....

결과가 여러 개인 경우 주문없이 FirstOrDefault는 서버에서 사용 된 인덱스 전략에 따라 다른 결과를 다시 가져올 수 있습니다.

개인적으로 개발자는 결과에 신경 쓰지 않는다고 말하면서 코드에서 FirstOrDefault를 볼 수 없습니다. 주문을 통해 최신 / 가장 이른 것을 시행하는 방법으로 유용 할 수 있습니다. FirstOrDefault를 사용하는 부주의 한 개발자로 인한 많은 문제를 해결해야했습니다.


-2

GitHub에서 다양한 방법을 사용하도록 Google에 문의했습니다. 각 방법에 대해 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 |

-8

를 사용 FirstOrDefault(x=> x.ID == key)하면 결과가 훨씬 빨리 검색 될 수있을 때 왜 사용하고 있는지 이해할 수 없습니다 Find(key). 테이블의 기본 키를 사용하여 쿼리하는 경우 경험상 항상을 사용하는 것 Find(key)입니다. 등의 FirstOrDefault술어에 사용되어야합니다 (x=> x.Username == username).

질문의 제목이 DB의 linq 또는 Linq to List / IEnumerable 등에 특정하지 않았기 때문에 이것은 하향 투표를받을 자격이 없었습니다.


1
어떤 네임 스페이스가 Find()있습니까?
p.campbell

제발 말해 줄래? 아직도 답변을 기다리고 있습니다.
데니

다음과 같을 수 있습니다 : stackoverflow.com/questions/14032709/…
Jeppe

"IEnumerable"이라는 단어는 질문 본문의 첫 줄에 있습니다. 실제 질문이 아닌 제목 만 읽고 결과에 잘못된 답변을 게시 한 경우, 이는 실수 이며 IMO를 공감해야 할 정당한 이유입니다.
F1Krazy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.