재우니의 블로그



C#에서 IQueryable과 IEnumerable은 LINQ 쿼리를 실행하기 위해 사용되는 두 가지 인터페이스입니다. 이 두 인터페이스는 데이터 소스로부터 데이터를 쿼리하는 방법에 따라 다르게 작동합니다.

 

IEnumerable은 .NET Framework에서 가장 일반적으로 사용되는 인터페이스 중 하나입니다. 이 인터페이스는 컬렉션의 모든 항목을 반복하고 각 항목에 대한 단순한 foreach 루프를 제공합니다. IEnumerable은 일반적으로 메모리 내 컬렉션에서 사용됩니다. IEnumerable은 모든 데이터를 메모리로 가져와서 필터링, 정렬 및 기타 작업을 수행합니다. 따라서 대량의 데이터 집합에서 작업하는 경우 많은 메모리를 소비하고 성능에 영향을 미칠 수 있습니다.


IEnumerable은 Loop 를 사용하여 데이터 소스에서 순차적으로 데이터를 검색합니다. IEnumerable은 데이터를 메모리에 미리로드하여 쿼리를 수행하므로 쿼리 수행 시 모든 데이터를 가져오기 때문에 메모리 사용량이 높아질 수 있습니다. 이러한 이유로 IEnumerable은 작은 데이터셋에서 사용할 때 유용합니다.

 

 


IQueryable은 IEnumerable와 매우 유사하지만 더 많은 유연성을 제공합니다. LINQ 쿼리를 빌드하고 데이터베이스와 같은 외부 데이터 소스에 대한 쿼리를 빌드하기 위한 인터페이스입니다.  IQueryable은 일반적으로 데이터 소스에서 직접 작업을 수행합니다. 이는 데이터베이스, 웹 서비스 또는 기타 데이터 원본과 같은 데이터 저장소에서 데이터를 검색하는 경우 특히 유용합니다. 또한 LINQ(언어 통합 쿼리)를 사용하여 데이터 원본에서 쿼리를 실행할 수 있습니다. LINQ는 IQueryable과 함께 사용될 때 쿼리를 최적화하고 데이터베이스 쿼리의 일부로 변환하는 기능을 제공합니다. 이는 대량의 데이터를 처리할 때 성능과 메모리 사용량을 최적화할 수 있는 장점을 제공합니다.


즉, IQueryable은 쿼리를 구성한 후에도 데이터 소스로부터 데이터를 검색하지 않습니다. 대신, 데이터 소스에 대한 쿼리를 작성하고 데이터를 검색하기 위한 적절한 메서드를 호출하여 데이터를 가져옵니다. 이 방법은 데이터베이스와 같은 대규모 데이터셋에서 쿼리를 수행할 때 효율적입니다.

 



다음은 IQueryable과 IEnumerable의 예제입니다.

IEnumerable

var list = new List<int> { 1, 2, 3, 4, 5 };
var query = from i in list
            where i > 3
            select i;
foreach (var item in query)
{
    Console.WriteLine(item);
}

IQueryable

var dbContext = new MyDbContext();
var query = from c in dbContext.Customers
            where c.City == "New York"
            select c;
foreach (var item in query)
{
    Console.WriteLine(item.Name);
}


위 예제에서, IEnumerable은 메모리에서 데이터를 가져오므로 작은 데이터셋에서 사용할 때 유용합니다. IQueryable은 데이터베이스와 같은 대규모 데이터셋에서 쿼리를 수행할 때 효율적입니다.

 

 

IQueryable은 IQueryable<T> 인터페이스를 사용하여 정의됩니다. 따라서 IQueryable을 사용하려면 데이터 소스에 대한 IQueryable<T> 인터페이스를 구현해야 합니다.

 

public class ProductRepository : IQueryable<Product>
{
    private readonly DbContext dbContext;

    public ProductRepository(DbContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public IEnumerator<Product> GetEnumerator()
    {
        return dbContext.Products.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public Type ElementType => typeof(Product);

    public Expression Expression => dbContext.Products.Expression;

    public IQueryProvider Provider => dbContext.Products.Provider;
}

 

위 예제는 Product 데이터 소스에 대한 ProductRepository 클래스를 구현하는 방법을 보여줍니다. ProductRepository 클래스는 IQueryable<Product> 인터페이스를 구현하므로 IQueryable을 사용하여 Product 데이터를 검색할 수 있습니다.

 

var repository = new ProductRepository(dbContext);

var query = from p in repository
            where p.Category == "Books"
            select p;

foreach (var product in query)
{
    Console.WriteLine(product.Name);
}

 

이 예제에서는 Product 데이터 소스에 대한 IQueryable<T> 인터페이스를 구현하는 방법을 보여주며 IQueryable을 사용하여 데이터를 검색하는 방법을 보여줍니다.

 

IEnumerable과 IQueryable은 모두 C#에서 데이터를 검색하고 조작하는 데 사용됩니다. 하지만 두 인터페이스는 목적과 사용 방법에 차이가 있습니다. IEnumerable은 일반적으로 메모리 내 컬렉션에서 사용되며 IQueryable은 데이터베이스, 웹 서비스 또는 기타 데이터 원본과 같은 데이터 저장소에서 데이터를 검색하는 경우 특히 유용합니다.

따라서 데이터 검색에 대한 작업을 수행할 때 해당 데이터 소스의 특성에 따라 적절한 인터페이스를 선택하고 적절한 방법으로 사용하는 것이 중요합니다.

 

 

페이징 처리

 

페이징 처리는  Count() 메소드를 사용하여 전체 데이터의 수를 가져온 후, Skip() 및 Take() 메소드를 사용하여 특정 페이지의 항목을 가져옵니다. 이를 ToList() 메소드를 사용하여 최종 결과를 가져옵니다.

 

using System;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        using (var db = new NorthwindEntities())
        {
            int pageSize = 10;
            int pageNumber = 3;

            var query = db.Orders.OrderBy(o => o.OrderDate);

            int totalCount = query.Count();

            var orders = query.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();

            Console.WriteLine("Page {0} of {1}", pageNumber, Math.Ceiling((double)totalCount / pageSize));

            foreach (var order in orders)
            {
                Console.WriteLine("{0} - {1}", order.OrderID, order.OrderDate);
            }
        }
    }
}