[C#] IEnumerable <T> 대 IQueryable <T> 반환

귀국 IQueryable<T>과 귀국의 차이점은 무엇입니까 IEnumerable<T>?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;



답변

예, 둘 다 실행 연기 를 제공합니다 .

차이점은 IQueryable<T>LINQ-to-SQL (LINQ.-to-anything)이 작동하도록하는 인터페이스입니다. 따라서에 대한 쿼리를 더 세분화 IQueryable<T>하면 가능한 경우 해당 쿼리가 데이터베이스에서 실행됩니다.

IEnumerable<T>경우 LINQ-to-Object가되므로 원래 쿼리와 일치하는 모든 개체를 데이터베이스에서 메모리로로드해야합니다.

코드에서 :

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

이 코드는 SQL을 실행하여 골드 고객 만 선택합니다. 반면 다음 코드는 데이터베이스에서 원래 쿼리를 실행 한 다음 메모리에서 비 골드 고객을 필터링합니다.

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

이것은 매우 중요한 차이점이며, 작업을 IQueryable<T>하면 데이터베이스에서 너무 많은 행을 리턴하지 않아도됩니다. 또 다른 주요 예는 페이징을 수행하는 것입니다. Takeand Skipon 을 사용 IQueryable하면 요청 된 행 수만 가져옵니다. 이 작업을 수행 IEnumerable<T>하면 모든 행이 메모리에로드됩니다.


답변

가장 좋은 대답은 좋지만 두 인터페이스가 어떻게 다른지 설명하는 표현 트리는 언급하지 않습니다. 기본적으로 두 개의 동일한 LINQ 확장 세트가 있습니다. Where(), Sum(), Count(), FirstOrDefault()기능을 받아들이와 표현을 허용 하나, 등 모두 두 가지 버전이있다.

  • IEnumerable버전의 서명은 다음과 같습니다Where(Func<Customer, bool> predicate)

  • IQueryable버전의 서명은 다음과 같습니다Where(Expression<Func<Customer, bool>> predicate)

둘 다 동일한 구문을 사용하여 호출되기 때문에 실현하지 않고 두 가지를 모두 사용했을 것입니다.

예를 들어,이 Where(x => x.City == "<City>")모두에서 작동 IEnumerable하고IQueryable

  • 사용시 Where()IEnumerable컬렉션 컴파일러로 컴파일 된 함수를 전달Where()

  • 사용하는 경우 Where()IQueryable모음, 컴파일러에 식 트리를 전달합니다 Where(). 표현식 트리는 리플렉션 시스템과 유사하지만 코드 용입니다. 컴파일러는 코드를 쉽게 소화 할 수있는 형식으로 코드의 기능을 설명하는 데이터 구조로 코드를 변환합니다.

왜이 표현 트리에 신경을 쓰나요? Where()데이터를 필터링 하고 싶습니다 .
주된 이유는 EF와 Linq2SQL ORM이 모두 표현식 트리를 SQL로 직접 변환하여 코드가 훨씬 빠르게 실행될 수 있기 때문입니다.

아, 그것은 무료 성능 향상처럼 들립니다 AsQueryable(). 그 경우 어디에서나 사용해야 합니까?
아니요, IQueryable기본 데이터 공급자가 무언가를 할 수있는 경우에만 유용합니다. 정기적처럼 뭔가를 변환 List하는 것은 IQueryable당신에게 어떤 혜택을 제공하지 않습니다.


답변

예, 둘 다 지연된 실행을 사용합니다. SQL Server 프로파일 러를 사용하여 차이점을 설명하겠습니다 ….

다음 코드를 실행할 때 :

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

SQL Server 프로파일 러에서 다음과 같은 명령을 찾습니다.

"SELECT * FROM [dbo].[WebLog]"

1 백만 개의 레코드가있는 WebLog 테이블에 대해 해당 코드 블록을 실행하는 데 약 90 초가 걸립니다.

따라서 모든 테이블 레코드는 메모리에 객체로로드 된 다음 각 .Where ()와 함께 이러한 객체에 대한 메모리의 또 다른 필터가됩니다.

위의 예 (두 번째 줄) IQueryable대신에 사용할 경우 IEnumerable:

SQL Server 프로파일 러에서 다음과 같은 명령을 찾습니다.

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

를 사용하여이 코드 블록을 실행하는 데 약 4 초가 걸립니다 IQueryable.

IQueryable에는 예제에서 지연된 실행이라고하는 Expression사용시 생성되기 시작하는 트리 표현식을 저장 하는 속성이 있으며 result,이 표현식은 데이터베이스 엔진에서 실행되도록 SQL 쿼리로 변환됩니다.


답변

둘 다 당신에게 연기 된 집행을 제공 할 것입니다.

어느 것이 다른 것보다 선호되는지는 기본 데이터 소스가 무엇인지에 달려 있습니다.

를 반환하면 IEnumerable런타임에서 LINQ to Objects를 사용하여 컬렉션을 쿼리합니다.

를 반환하면 IQueryable( IEnumerable실제로 구현 ) 쿼리를 기본 소스 (LINQ to SQL, LINQ to XML 등)에서 더 잘 수행 할 수있는 것으로 변환하는 추가 기능을 제공합니다.


답변

일반적으로 다음을 권장합니다.

  • 메소드 IQueryable<T>를 사용하여 개발자가 실행하기 전에 리턴하는 쿼리를 세분화 할 수있게하려면 리턴하십시오.

  • IEnumerable열거 할 객체 세트를 전송하려면 리턴 하십시오.

IQueryable데이터에 대한 “쿼리”(원하는 경우 구체화 할 수 있음)를 상상해보십시오 . 은 IEnumerable당신이 열거 할 수있는 이상 (이미 수신 된 또는 생성 된) 개체의 집합입니다.


답변

이전에 많은 이야기가 있지만 더 기술적 인 방법으로 뿌리로 돌아 왔습니다.

  1. IEnumerable 는 열거 할 수있는 메모리에있는 객체의 모음입니다. 메모리 내에서 반복 할 수있는 메모리 내 시퀀스입니다 ( foreach루프 내에서 쉽게 사용할 수 있지만, 계속 사용할 수는 IEnumerator있음). 그것들은 그대로 메모리에 상주합니다.
  2. IQueryable 최종 결과에 대해 열거 할 수있는 능력을 가진 어떤 시점에서 다른 것으로 번역 될 표현 트리 입니다 . 나는 이것이 대부분의 사람들을 혼란스럽게하는 것 같아요.

그들은 분명히 다른 의미를 가지고 있습니다.

IQueryableLINQ 집계 함수 (Sum, Count 등) 또는 ToList [Array, Dictionary 등의 릴리스 API가 호출 되 자마자 기본 쿼리 공급자가 다른 것으로 변환 할 식 트리 (단순히 쿼리)를 나타냅니다. ..]. 그리고 IQueryable또한 구현하는 객체 IEnumerable, IEnumerable<T>그래서 그들은 쿼리를 표현하는 경우 해당 쿼리의 결과를 반복 할 수있다. 이는 IQueryable이 쿼리 일 필요는 없음을 의미합니다. 올바른 용어는 표현 트리 입니다.

이제 이러한 표현식이 실행되는 방식과 전환 대상은 모두 쿼리 제공자 (우리가 생각할 수있는 표현식 실행기)에 달려 있습니다.

에서 엔티티 프레임 워크 (즉 신비 기본 데이터 소스 제공 업체 또는 쿼리 제공 업체입니다) 세계 IQueryable표현식은 네이티브로 번역되는 T-SQL의 쿼리. Nhibernate그들과 비슷한 일을합니다. 예를 들어 LINQ : IQueryable Provider 링크 작성에 설명 된 개념에 따라 고유 한 것을 작성할 수 있으며 제품 상점 제공자 서비스에 대한 사용자 정의 조회 API를 원할 수 있습니다.

따라서 기본적으로 IQueryable객체는 명시 적으로 릴리스하고 시스템에 객체를 SQL 등으로 다시 작성하고 이후 처리를 위해 실행 체인을 보내도록 지시 할 때까지 오래 지속됩니다.

실행 을 연기하는 것처럼 LINQ특정 API가 시퀀스 (동일한 Count, ToList 등)에 대해 호출 될 때마다 메모리에서 표현식 트리 체계를 유지하고 필요할 때만 실행으로 전송하는 기능입니다.

두 가지 모두의 올바른 사용법은 특정 사례에 직면 한 작업에 따라 크게 다릅니다. 잘 알려진 리포지토리 패턴의 경우 개인적으로을 반환하도록 선택합니다 IList. 즉 IEnumerable목록 (인덱서 등)입니다. 따라서 IQueryable코드의 다른 곳에서 저장소 및 IEnumerable 내에서만 사용하는 것이 좋습니다. 우려 분리 원칙을 IQueryable세분화하고 파괴 하는 테스트 가능성 우려에 대해서는 언급하지 않습니다 . 리포지토리 내에서 식을 반환하면 소비자는 원하는대로 지속성 계층을 사용할 수 있습니다.

엉망에 약간의 추가 🙂 (댓글의 토론에서)) 실제 유형이 아니기 때문에 메모리에있는 객체는 없습니다. 유형에 대한 마커입니다. 그러나 그것은 의미가 있습니다 (심지어 이유입니다 MSDN은 그것을 이런 식으로 넣어) 식 트리로 IQueryables 반면 메모리 컬렉션으로 IEnumerables 생각. 요점은 IQueryable 인터페이스가 IEnumerable 인터페이스를 상속하므로 쿼리를 나타내는 경우 해당 쿼리 결과를 열거 할 수 있다는 것입니다. 열거하면 IQueryable 개체와 관련된 식 트리가 실행됩니다. 따라서 실제로 메모리에 객체가 없으면 IEnumerable 멤버를 호출 할 수 없습니다. 어쨌든 비어 있지 않으면 거기에 들어갑니다. IQueryables는 데이터가 아니라 쿼리 일뿐입니다.


답변

일반적으로 쿼리의 원래 정적 유형은 중요 할 때까지 유지하려고합니다.

이러한 이유로 IQueryable<>또는 대신 변수를 ‘var’로 정의 할 수 IEnumerable<>있으며 유형이 변경되지 않음을 알 수 있습니다.

로 시작하는 경우 IQueryable<>일반적 IQueryable<>으로을 변경해야 할 몇 가지 이유가있을 때까지 이를 유지하려고 합니다. 그 이유는 가능한 많은 정보를 쿼리 프로세서에 제공하기 때문입니다. 예를 들어 10 개의 결과 ()를 사용 Take(10)하려는 경우 쿼리 계획을 최적화하고 사용할 데이터 만 보낼 수 있도록 SQL Server가 그 사실을 알기를 원합니다.

에서 눈길을 끄는 이유는 유형을 변경하기 IQueryable<>IEnumerable<>당신의 구현 몇 가지 확장 기능을 호출하는 것을 수 있습니다 IQueryable<>특정 객체가 비효율적으로 처리하거나 핸들 할 수없는 하나를. 이 경우 호출하는 확장 함수가 클래스 대신 클래스 의 함수가되도록 유형을 IEnumerable<>(변수 유형에 할당 IEnumerable<>하거나 AsEnumerable확장 메서드를 사용하여) 로 변환 할 수 있습니다 .EnumerableQueryable