이것에 대해 할 말이 많습니다. 저에 집중하자 AsEnumerable
및 AsQueryable
및 언급 ToList()
길을 따라.
이 방법들은 무엇을 하는가?
AsEnumerable
및 AsQueryable
캐스트 또는 변환에 IEnumerable
또는 IQueryable
각각. 나는 이유를 가지고 캐스트하거나 변환 한다고 말합니다 .
소스 객체가 이미 대상 인터페이스를 구현하면 소스 객체 자체가 반환되지만 대상 인터페이스로 캐스트 됩니다. 즉, 유형은 변경되지 않지만 컴파일 타임 유형은 변경됩니다.
소스 객체가 대상 인터페이스를 구현하지 않으면 소스 객체가 대상 인터페이스를 구현하는 객체로 변환 됩니다. 따라서 유형과 컴파일 타임 유형이 모두 변경됩니다.
몇 가지 예를 보여 드리겠습니다. 컴파일 타임 유형과 객체의 실제 유형 ( Jon Skeet 제공 ) 을보고하는이 작은 방법이 있습니다 .
void ReportTypeProperties<T>(T obj)
{
Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}
의 임의 LINQ - 투 - SQL 해보자 Table<T>
, 구현을 IQueryable
:
ReportTypeProperties(context.Observations);
ReportTypeProperties(context.Observations.AsEnumerable());
ReportTypeProperties(context.Observations.AsQueryable());
결과:
Compile-time type: Table`1
Actual type: Table`1
Compile-time type: IEnumerable`1
Actual type: Table`1
Compile-time type: IQueryable`1
Actual type: Table`1
테이블 클래스 자체는 항상 반환되지만 해당 표현은 변경됩니다.
이제가 IEnumerable
아닌 을 구현하는 객체 IQueryable
:
var ints = new[] { 1, 2 };
ReportTypeProperties(ints);
ReportTypeProperties(ints.AsEnumerable());
ReportTypeProperties(ints.AsQueryable());
결과 :
Compile-time type: Int32[]
Actual type: Int32[]
Compile-time type: IEnumerable`1
Actual type: Int32[]
Compile-time type: IQueryable`1
Actual type: EnumerableQuery`1
거기는. AsQueryable()
배열을로 변환 했습니다 . " 컬렉션을 데이터 소스 로 EnumerableQuery
나타냅니다 ." (MSDN).IEnumerable<T>
IQueryable<T>
용도는 무엇입니까?
AsEnumerable
는 IQueryable
구현이 LINQ에서 객체 (L2O) 로 전환하는 데 자주 사용됩니다 . 대부분 전자가 L2O의 기능을 지원하지 않기 때문입니다. 자세한 내용은 LINQ 엔터티에 AsEnumerable ()의 영향 은 무엇입니까?를 참조하십시오 . .
예를 들어, Entity Framework 쿼리에서는 제한된 수의 메서드 만 사용할 수 있습니다. 예를 들어 쿼리에서 자체 메소드 중 하나를 사용해야하는 경우 일반적으로 다음과 같이 작성합니다.
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => MySuperSmartMethod(x))
ToList
- 변환 IEnumerable<T>
A를 List<T>
- 자주뿐만 아니라이 목적을 위해 사용됩니다. 사용의 장점 AsEnumerable
대는 ToList
것입니다 AsEnumerable
쿼리를 실행하지 않습니다. AsEnumerable
지연된 실행을 유지하고 종종 쓸모없는 중간 목록을 작성하지 않습니다.
반면에 LINQ 쿼리를 강제로 실행하려는 경우이를 수행 ToList
할 수 있습니다.
AsQueryable
열거 가능한 컬렉션이 LINQ 문에서 식을 허용하도록하는 데 사용할 수 있습니다. 자세한 내용은 여기를 참조하십시오. 콜렉션에 AsQueryable ()을 사용해야합니까?.
약물 남용에 대한 참고 사항!
AsEnumerable
마약처럼 작동합니다. 빠른 수정이지만 비용이 많이 들고 근본적인 문제를 해결하지 못합니다.
많은 스택 오버플로 답변 AsEnumerable
에서 LINQ 표현식의 지원되지 않는 메소드 관련 문제를 해결하기 위해 신청 하는 사람들이 있습니다. 그러나 가격이 항상 명확한 것은 아닙니다. 예를 들어, 이렇게하면 :
context.MyLongWideTable // A table with many records and columns
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate })
... 모든 것이 깔끔하게 SQL 문으로 변환되어 ( ) 및 프로젝트 ( ) 를 필터링 합니다. 즉, SQL 결과 세트의 길이와 너비가 각각 줄어 듭니다.Where
Select
이제 사용자가의 날짜 부분 만보고 싶어한다고 가정합니다 CreateDate
. Entity Framework에서 다음을 신속하게 발견 할 수 있습니다.
.Select(x => new { x.Name, x.CreateDate.Date })
... (작성 당시)는 지원되지 않습니다. 아, 다행스럽게도 AsEnumerable
수정 사항이 있습니다.
context.MyLongWideTable.AsEnumerable()
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate.Date })
물론, 아마도 작동합니다. 그러나 전체 테이블을 메모리로 가져온 다음 필터와 프로젝션을 적용합니다. 글쎄, 대부분의 사람들은 Where
첫 번째 일을 할만 큼 똑똑합니다 .
context.MyLongWideTable
.Where(x => x.Type == "type").AsEnumerable()
.Select(x => new { x.Name, x.CreateDate.Date })
그러나 여전히 모든 열을 먼저 가져오고 투영은 메모리에서 수행됩니다.
실제 수정 사항은 다음과 같습니다.
context.MyLongWideTable
.Where(x => x.Type == "type")
.Select(x => new { x.Name, DbFunctions.TruncateTime(x.CreateDate) })
(그러나 그것은 조금 더 지식이 필요합니다 ...)
이 방법들은 무엇을하지 않습니까?
IQueryable 기능 복원
이제 중요한 경고입니다. 당신이 할 때
context.Observations.AsEnumerable()
.AsQueryable()
로 표시된 소스 객체로 끝납니다 IQueryable
. (두 방법 모두 캐스트하고 변환하지 않기 때문에).
하지만 할 때
context.Observations.AsEnumerable().Select(x => x)
.AsQueryable()
결과는 어떻습니까?
는을 Select
생성합니다 WhereSelectEnumerableIterator
. 이것은 그 구현 내부 닷넷 클래스 IEnumerable
, 없습니다IQueryable
. 따라서 다른 유형으로의 변환이 수행되었으며AsQueryable
소스는 더 이상 원래 소스를 리턴 할 수 없습니다.
이것의 의미는 사용하는 AsQueryable
것이 특정 기능을 가진 쿼리 공급자 를 열거 형 으로 마술처럼 주입하는 방법 이 아니라는 것 입니다. 당신이한다고 가정
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => x.ToString())
.AsQueryable()
.Where(...)
where 조건은 SQL로 변환되지 않습니다. AsEnumerable()
LINQ 문 다음에 엔터티 프레임 워크 쿼리 공급자와의 연결이 결정됩니다.
예를 들어 사람들 Include
이 호출하여 기능을 컬렉션에 '주입'하려는 질문을 보았 기 때문에 의도적 으로이 예제를 보여줍니다 AsQueryable
. 컴파일하고 실행하지만 기본 개체에Include
더 이상 구현 수행하지 않습니다.
실행
두 AsQueryable
및 AsEnumerable
실행 (또는하지 않는 열거 소스 객체). 유형이나 표현 만 변경합니다. 관련된 인터페이스 IQueryable
와 IEnumerable
는 "열거되는 열거"에 지나지 않습니다. 예를 들어 위에서 언급 한 것처럼을 호출하여 강제로 실행되기 전에는 실행되지 않습니다 ToList()
.
즉 , 객체 IEnumerable
를 호출 하여 얻은 것을 실행하면 기본을 실행합니다 . 의 후속 실행 은 다시을 실행합니다 . 매우 비쌀 수 있습니다.AsEnumerable
IQueryable
IQueryable
IEnumerable
IQueryable
특정 구현
지금까지 이것은 Queryable.AsQueryable
및 Enumerable.AsEnumerable
확장 방법에 관한 것입니다. 물론 누구나 같은 이름 (및 함수)으로 인스턴스 메소드 또는 확장 메소드를 작성할 수 있습니다.
실제로 특정 AsEnumerable
확장 방법 의 일반적인 예 는 DataTableExtensions.AsEnumerable
입니다. 또는을 DataTable
구현하지 않으므로 일반 확장 방법이 적용되지 않습니다.IQueryable
IEnumerable