Linq와 함께 IQueryable 사용


256

IQueryableLINQ와 관련 하여 어떤 용도로 사용 됩니까?

확장 방법이나 다른 목적을 개발하는 데 사용됩니까?

답변:


507

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 개의 레코드 만 반환 할 수 있습니다.


9
"GetQueryableProducts ();"의 정의는 어디에 있습니까?
Pankaj

12
@StackOverflowUser 그것은 IQueryable<Product>당신의 ORM이나 저장소에 특정한 것- 을 반환하는 모든 방법을 의도합니다 .
Reed Copsey

권리. 그러나이 함수 호출 후 where 절을 언급했습니다. 따라서 시스템은 여전히 ​​필터를 인식하지 못합니다. 나는 여전히 모든 제품 레코드를 가져옵니다. 권리?
Pankaj

@StackOverflowUser 아니요-IQueryable <T>의 아름다움입니다 . 결과를 얻을 때 평가하도록 설정할 수 있습니다. 즉, 사실 후에 사용 된 Where 절은 여전히 ​​서버에서 실행되는 SQL 문으로 변환됩니다. ... 회선을 통해에만 필요한 요소를 당겨
리드 Copsey에게

40
@Testing 당신은 실제로 여전히 DB에 가지 않을 것입니다. 실제로 결과를 열거 할 때까지 (예 : use foreach또는 call ToList()) 실제로 DB에 도달하지는 않습니다.
리드 콥시

188

본질적으로 그 작업은 IEnumerable<T>쿼리 가능한 데이터 소스를 나타내는 것과 매우 유사 합니다. 차이점은 다양한 LINQ 메소드 Queryable가 더 구체적 일 수 있다는 Expression것입니다. 대리인이 아닌 트리를 사용하여 쿼리를 작성 하는 것입니다.Enumerable 입니다.

표현식 트리는 선택한 LINQ 공급자가 검사 할 수 있으며 실제 쿼리 로 바뀔 수 있습니다.

이것은 정말 아래이고 ElementType, ExpressionProvider- 그러나 실제로는 거의 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 });

중요하게, 정적 QueryableIL 의 정적 메소드 는 일반 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 "을 참조하십시오.이 두 가지 주제를 모두 다루고 있습니다.


2
마음에 들지 않으면 간단한 이해할 수있는 예 (시간이 있다면)로 저를 업데이트 할 수 있습니까?
user190560

"GetQueryableProducts ()의 정의는 어디에 있습니까? "?" Mr Reed Copsey의 답글
Pankaj

표현을 쿼리로 번역하는 것을
즐겼

델리게이트를 사용했다면 왜 불가능할까요?
David Klempfner

1
@Backwards_Dave 델리게이트가 (필수적으로) IL을 가리키고 있기 때문에 IL은 표현력이 충분하지 않아 SQL을 작성하기 위해 의도를 해체하려고 시도하기에 충분하지 않습니다. IL은 너무 많은 것들을 허용합니다. 즉, IL로 표현 될 수있는 대부분의 것들은 SQL과 같은 것으로 바꾸는 것이 합리적이라는 제한된 구문으로 표현 될 수 없었 습니다
Marc Gravell

17

하지만 리드 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> GetEmployeeAndPersonDetailIQueryableIEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerableof 모두에 SomeServiceClass공통 줄이 하나 포함되어 있기 때문에

where employeesToCollect.Contains(e.PersonId)

실행 부분 AND (N'M' = [Extent1].[Gender])에서 왜 누락 된 부분 보다 IEnumerable두 함수 호출에서 Where(i => i.Gender == "M") inprogram.cs 를 사용했습니다.

차이가 사이에 출처 이제 우리는 시점에 IQueryableIEnumerable

어떤 엔티티 프레임 워크가 IQueryable메소드가 호출 메소드 내부에 작성된 linq 문을 사용하여 더 많은 linq 표현식이 결과 세트에 정의되어 있는지 확인한 다음 결과를 가져와 더 적절한 SQL을 생성해야 할 때까지 정의 된 모든 linq 쿼리를 수집합니다. 실행할 쿼리.

다음과 같은 많은 이점을 제공합니다.

  • 전체 linq 쿼리 실행으로 유효 할 수있는 SQL Server로 채워진 행만
  • 불필요한 행을 선택하지 않아 SQL Server 성능 향상
  • 네트워크 비용 절감

여기 예제와 같이 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)볼 수 있습니다.


2

추가로 줄을 추가로 쿼리 할 수 ​​있습니다. 이것이 서비스 경계를 ​​넘어 서면이 IQueryable 객체의 사용자는 더 많은 것을 할 수 있습니다.

예를 들어, nhibernate와 함께 지연 로딩을 사용하는 경우 필요할 때 / 필요할 때 그래프가로드 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.