답변:
Marc Gravell의 답변 은 매우 완벽하지만 사용자 관점에서 이것에 대해 뭔가를 추가 할 것이라고 생각했습니다 ...
사용자 관점에서 볼 때 가장 큰 차이점 IQueryable<T>
은 (사물을 올바르게 지원하는 공급자와 함께) 사용할 때 많은 리소스를 절약 할 수 있다는 것입니다.
예를 들어, ORM 시스템이 많은 원격 데이터베이스에 대해 작업하는 경우 테이블에서 데이터를 페치하는 방법 IEnumerable<T>
과로 리턴하는 옵션이 IQueryable<T>
있습니다. 예를 들어 Products 테이블이 있고 비용이 $ 25 이상인 모든 제품을 원한다고 가정합니다.
당신이 할 경우 :
IEnumerable<Product> products = myORM.GetProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
여기서 발생하는 것은 데이터베이스가 모든 제품을로드하고 와이어를 통해 프로그램으로 전달하는 것입니다. 그런 다음 프로그램이 데이터를 필터링합니다. 본질적으로 데이터베이스 SELECT * FROM Products
는를 수행하고 모든 제품을 사용자에게 반환합니다.
IQueryable<T>
반면 올바른 제공 업체를 통해 다음을 수행 할 수 있습니다.
IQueryable<Product> products = myORM.GetQueryableProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
코드는 동일하게 보이지만 여기서 실행되는 SQL은입니다 SELECT * FROM Products WHERE Cost >= 25
.
POV는 개발자로서 동일하게 보입니다. 그러나 성능 관점에서 20,000 개 대신 네트워크를 통해 2 개의 레코드 만 반환 할 수 있습니다.
IQueryable<Product>
당신의 ORM이나 저장소에 특정한 것- 을 반환하는 모든 방법을 의도합니다 .
foreach
또는 call ToList()
) 실제로 DB에 도달하지는 않습니다.
본질적으로 그 작업은 IEnumerable<T>
쿼리 가능한 데이터 소스를 나타내는 것과 매우 유사 합니다. 차이점은 다양한 LINQ 메소드 Queryable
가 더 구체적 일 수 있다는 Expression
것입니다. 대리인이 아닌 트리를 사용하여 쿼리를 작성 하는 것입니다.Enumerable
입니다.
표현식 트리는 선택한 LINQ 공급자가 검사 할 수 있으며 실제 쿼리 로 바뀔 수 있습니다.
이것은 정말 아래이고 ElementType
, Expression
및 Provider
- 그러나 실제로는 거의 A와 이것에 대해 신경 쓸 필요가없는 사용자 . LINQ 구현 자만이 세부 정보를 알아야합니다.
다시 의견; 나는 당신이 원하는 것을 확실하지 않지만 LINQ-to-SQL을 고려하십시오. 여기서 중심 객체는 DataContext
데이터베이스 래퍼를 나타내는입니다. 일반적으로 테이블 당 속성 (예 Customers
:)이 있고 테이블이 구현 IQueryable<Customer>
합니다. 그러나 우리는 그것을 직접 사용하지 않습니다. 치다:
using(var ctx = new MyDataContext()) {
var qry = from cust in ctx.Customers
where cust.Region == "North"
select new { cust.Id, cust.Name };
foreach(var row in qry) {
Console.WriteLine("{0}: {1}", row.Id, row.Name);
}
}
이것은 (C # 컴파일러에 의해)됩니다 :
var qry = ctx.Customers.Where(cust => cust.Region == "North")
.Select(cust => new { cust.Id, cust.Name });
이는 C # 컴파일러에 의해 다시 다음과 같이 해석됩니다.
var qry = Queryable.Select(
Queryable.Where(
ctx.Customers,
cust => cust.Region == "North"),
cust => new { cust.Id, cust.Name });
중요하게, 정적 Queryable
IL 의 정적 메소드 는 일반 IL 대신 오브젝트 트리로 컴파일되는 표현식 트리를 사용합니다. 예를 들어 "Where"를 보면 다음과 비슷한 것을 얻을 수 있습니다.
var cust = Expression.Parameter(typeof(Customer), "cust");
var lambda = Expression.Lambda<Func<Customer,bool>>(
Expression.Equal(
Expression.Property(cust, "Region"),
Expression.Constant("North")
), cust);
... Queryable.Where(ctx.Customers, lambda) ...
컴파일러가 우리를 위해 많은 일을하지 않았습니까? 이 객체 모델은 분리되어 의미가 무엇인지 검사 한 후 TSQL 생성기에서 다시 정리할 수 있습니다.
SELECT c.Id, c.Name
FROM [dbo].[Customer] c
WHERE c.Region = 'North'
(문자열은 매개 변수로 끝날 수 있습니다. 기억하지 못합니다)
델리게이트를 방금 사용한 경우에는이 중 어느 것도 가능하지 않습니다. 그리고 이것은Queryable
/ 의 요점입니다 IQueryable<T>
. 그것은 표현 트리를 사용하기위한 진입 점을 제공합니다.
이 모든 것이 매우 복잡하기 때문에 컴파일러가 우리를 위해 쉽고 편리하게 만드는 것이 좋습니다.
자세한 내용은 " C # in Depth "또는 " LINQ in Action "을 참조하십시오.이 두 가지 주제를 모두 다루고 있습니다.
하지만 리드 Copsey 와 마크 Gravell가 이미에 대한 설명 IQueryable
(도 IEnumerable
) 충분히 마일에 작은 예제를 제공하여 좀 더 여기에 추가 할 IQueryable
하고 IEnumerable
많은 사용자가 요청으로
예 : 데이터베이스에 두 개의 테이블을 만들었습니다.
CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Gender] [nchar](1) NOT NULL)
CREATE TABLE [dbo].[Person]([PersonId] [int] NOT NULL PRIMARY KEY,[FirstName] [nvarchar](50) NOT NULL,[LastName] [nvarchar](50) NOT NULL)
PersonId
테이블 의 기본 키 ( ) Employee
도 위조 키 (personid
)이기도합니다.Person
다음으로 내 응용 프로그램에 ado.net 엔터티 모델을 추가하고 그 아래에 서비스 클래스를 만듭니다.
public class SomeServiceClass
{
public IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable(IEnumerable<int> employeesToCollect)
{
DemoIQueryableEntities db = new DemoIQueryableEntities();
var allDetails = from Employee e in db.Employees
join Person p in db.People on e.PersonId equals p.PersonId
where employeesToCollect.Contains(e.PersonId)
select e;
return allDetails;
}
public IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable(IEnumerable<int> employeesToCollect)
{
DemoIQueryableEntities db = new DemoIQueryableEntities();
var allDetails = from Employee e in db.Employees
join Person p in db.People on e.PersonId equals p.PersonId
where employeesToCollect.Contains(e.PersonId)
select e;
return allDetails;
}
}
그들은 같은 linq를 포함합니다. program.cs
아래 정의 된대로 호출
class Program
{
static void Main(string[] args)
{
SomeServiceClass s= new SomeServiceClass();
var employeesToCollect= new []{0,1,2,3};
//IQueryable execution part
var IQueryableList = s.GetEmployeeAndPersonDetailIQueryable(employeesToCollect).Where(i => i.Gender=="M");
foreach (var emp in IQueryableList)
{
System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
}
System.Console.WriteLine("IQueryable contain {0} row in result set", IQueryableList.Count());
//IEnumerable execution part
var IEnumerableList = s.GetEmployeeAndPersonDetailIEnumerable(employeesToCollect).Where(i => i.Gender == "M");
foreach (var emp in IEnumerableList)
{
System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
}
System.Console.WriteLine("IEnumerable contain {0} row in result set", IEnumerableList.Count());
Console.ReadKey();
}
}
출력은 분명히 동일합니다.
ID:1, EName:Ken,Gender:M
ID:3, EName:Roberto,Gender:M
IQueryable contain 2 row in result set
ID:1, EName:Ken,Gender:M
ID:3, EName:Roberto,Gender:M
IEnumerable contain 2 row in result set
문제는 차이점이 무엇 / 어디에 있습니까? 차이가없는 것 같습니까? 정말!!
이 기간 동안 엔티티 프레임 워크 5에 의해 생성 및 실행 된 SQL 쿼리에 대해 살펴 보겠습니다.
iQueryable 실행 부분
--IQueryableQuery1
SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])
--IQueryableQuery2
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Employee] AS [Extent1]
WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])
) AS [GroupBy1]
IEnumerable 실행 부분
--IEnumerableQuery1
SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)
--IEnumerableQuery2
SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)
두 실행 부분에 대한 공통 스크립트
/* these two query will execute for both IQueryable or IEnumerable to get details from Person table
Ignore these two queries here because it has nothing to do with IQueryable vs IEnumerable
--ICommonQuery1
exec sp_executesql N'SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
--ICommonQuery2
exec sp_executesql N'SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3
*/
이제 몇 가지 질문이 있습니다. 추측하고 대답 해 드리겠습니다.
동일한 결과에 대해 다른 스크립트가 생성되는 이유는 무엇입니까?
여기서 몇 가지 요점을 찾아 보겠습니다.
모든 쿼리에는 공통된 부분이 하나 있습니다
WHERE [Extent1].[PersonId] IN (0,1,2,3)
왜? linq 쿼리에서 function IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable
과
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
of 모두에 SomeServiceClass
공통 줄이 하나 포함되어 있기 때문에
where employeesToCollect.Contains(e.PersonId)
실행 부분 AND (N'M' = [Extent1].[Gender])
에서 왜 누락 된 부분 보다
IEnumerable
두 함수 호출에서 Where(i => i.Gender == "M") in
program.cs 를 사용했습니다.
차이가 사이에 출처 이제 우리는 시점에
IQueryable
와IEnumerable
어떤 엔티티 프레임 워크가 IQueryable
메소드가 호출 메소드 내부에 작성된 linq 문을 사용하여 더 많은 linq 표현식이 결과 세트에 정의되어 있는지 확인한 다음 결과를 가져와 더 적절한 SQL을 생성해야 할 때까지 정의 된 모든 linq 쿼리를 수집합니다. 실행할 쿼리.
다음과 같은 많은 이점을 제공합니다.
여기 예제와 같이 SQL Server는 IQueryable 실행 후 두 행만 응용 프로그램으로 반환되었지만 IEnumerable 쿼리에 대해 세 행을 반환 한 이유는 무엇입니까?
의 경우에는 IEnumerable
방법, 엔티티 프레임 워크는 결과 필요가 가져올 때 방법 및 구조 SQL 쿼리 내부에 기록 된 LINQ 문을했다. SQL 쿼리를 구성하는 나머지 linq 부분은 포함되지 않습니다. 여기서처럼 열의 SQL Server에서는 필터링이 수행되지 않습니다 gender
.
그러나 출력은 동일합니까? 'IEnumerable은 SQL Server에서 결과를 검색 한 후 애플리케이션 레벨에서 결과를 더 필터링하기 때문에
그래서, 누군가는 무엇을 선택해야합니까? 나는 개인적으로 함수 결과를 정의하는 것을 선호 IQueryable<T>
이익을 많이 있기 때문에 그 이상이있다IEnumerable
두 개 이상의 IQueryable 함수를 결합하여 SQL 서버에보다 구체적인 스크립트를 생성 할 수 있습니다.
여기에서 예를 들어 내 관점에서 훨씬 더 적합한 IQueryable Query(IQueryableQuery2)
스크립트를 생성하는 것을 IEnumerable query(IEnumerableQuery2)
볼 수 있습니다.