IQueryable
3 min readIQueryable
TL;DR
IQueryable<T> looks like IEnumerable<T> but builds an expression tree instead of executing immediately, letting providers like EF Core translate the whole composed pipeline into a single SQL statement. Returning it from repositories pushes filtering, sorting, and projection to the database — the difference between SELECT TOP 100 ... WHERE Symbol = 'AAPL' and dragging millions of rows into memory before filtering them with LINQ-to-Objects.
How it works
IQueryable — Expression Trees for Remote Queries
"Use IQueryable
when queries should be translated and executed remotely (databases, APIs), not in memory."
❌ Bad example:
public IEnumerable<Trade> GetTradesForSymbol(string symbol)
{
return _dbContext.Trades.ToList() // loads ALL trades into memory
.Where(t => t.Symbol == symbol); // filters in memory
}
// Caller
var trades = repository.GetTradesForSymbol("AAPL"); // loads millions of rows
Using IEnumerable
âś… Good example:
public IQueryable<Trade> GetTradesForSymbol(string symbol)
{
return _dbContext.Trades.Where(t => t.Symbol == symbol); // builds expression tree
}
// Caller
var trades = repository.GetTradesForSymbol("AAPL")
.OrderByDescending(t => t.Timestamp)
.Take(100); // SQL: SELECT TOP 100 ... WHERE Symbol = 'AAPL' ORDER BY ...
👉 IQueryable
🔥 Composing queries before execution:
public IQueryable<Order> GetOrders()
{
return _dbContext.Orders;
}
// Caller composes query further
var recentLargeOrders = orderService.GetOrders()
.Where(o => o.Amount > 10000)
.Where(o => o.CreatedAt > DateTime.UtcNow.AddDays(-7))
.OrderByDescending(o => o.Amount); // still no execution
var results = recentLargeOrders.ToList(); // NOW executes as single SQL query
👉 Query composition is deferred until materialization (.ToList(), .First(), foreach).
🔥 Avoiding N+1 queries:
// ❌ Bad: N+1 problem
public IEnumerable<Order> GetOrdersWithCustomers()
{
var orders = _dbContext.Orders.ToList(); // 1 query
foreach (var order in orders)
{
var customer = order.Customer; // N queries (lazy loading)
}
return orders;
}
// âś… Good: single query with join
public IQueryable<Order> GetOrdersWithCustomers()
{
return _dbContext.Orders.Include(o => o.Customer); // SQL JOIN
}
👉 IQueryable
đź’ˇ In trading systems:
- Use IQueryable
for database repositories to push filtering/sorting to SQL. - Enable dynamic query composition for flexible reporting without loading everything.
- Avoid materializing with .ToList() prematurely—keep query deferred until final shape is known.