IQueryable<T> Interface

System.Linq
public interface IQueryable<out T> : IEnumerable<T>
Overview
Members
Remarks
Code Example

Summary

Represents a queryable collection of elements of a given type.

IQueryable<T> extends IEnumerable<T> by adding the ability to describe the query that produces the collection of data. This allows for deferred execution of the query and potential optimization by the query provider.

LINQ providers (such as Entity Framework) implement this interface to translate LINQ queries into their native query language (e.g., SQL) and execute them against a data source.

Remarks

An IQueryable<T> object typically represents a query against a data source. The query is expressed using LINQ methods and is not executed until the collection is enumerated (e.g., by using a `foreach` loop or by calling methods like `ToList()` or `ToArray()`).

When you create a LINQ query that targets an IQueryable<T> source, the query is built as an expression tree. This expression tree is then passed to the query provider, which translates it into a format that the underlying data source can understand and execute.

The out variance modifier on the generic type parameter `T` indicates that the type `T` is only produced by the interface and is not consumed. This allows an IQueryable<string> to be treated as an IEnumerable<object>, for example.

Members

The IQueryable<T> interface inherits from IEnumerable<T> and adds the following members:

Properties

  • Expression Expression

    Gets the expression tree associated with the data source.

  • Provider IQueryProvider

    Gets the query provider associated with this data source.

Methods (Inherited from IEnumerable<T>)

IQueryable<T> also inherits all members from IEnumerable<T>, including:

  • GetEnumerator()

Remarks

The primary purpose of IQueryable<T> is to enable LINQ queries to be translated and executed against various data sources by a query provider. This is fundamental to technologies like LINQ to SQL and Entity Framework.

When you use LINQ extension methods on an IQueryable<T> collection, these methods build an expression tree. For example, a `Where` clause becomes a Where expression in the tree.

The Expression property provides access to this expression tree, allowing the query provider to inspect and process the query logic. The Provider property returns an instance of IQueryProvider, which is responsible for executing the translated query.

Consider the following example:

var query = dbContext.Products.Where(p => p.Price > 100);

In this code snippet, query is an IQueryable<Product>. The Where method doesn't execute immediately; it modifies the expression tree associated with the query. When query is enumerated, the IQueryProvider associated with dbContext will translate the expression tree into SQL and execute it against the database.

Code Example

Here's a simple illustration of how IQueryable<T> might be used with a hypothetical data context.

Using LINQ with IQueryable<T>

using System; using System.Linq; using System.Collections.Generic; // Assume these classes are defined elsewhere public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public interface IQueryProvider { IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression); object Execute(System.Linq.Expressions.Expression expression); } public interface IQueryable<out T> : IEnumerable<T> { System.Linq.Expressions.Expression Expression { get; } IQueryProvider Provider { get; } } // A simple in-memory data context that implements IQueryable public class InMemoryProductRepository : IQueryable<Product> { private List<Product> _products; public System.Linq.Expressions.Expression Expression { get; } public IQueryProvider Provider { get; } public InMemoryProductRepository() { _products = new List<Product> { new Product { Id = 1, Name = "Laptop", Price = 1200.00m }, new Product { Id = 2, Name = "Mouse", Price = 25.50m }, new Product { Id = 3, Name = "Keyboard", Price = 75.00m }, new Product { Id = 4, Name = "Monitor", Price = 300.00m } }; // In a real scenario, Expression and Provider would be properly initialized this.Expression = System.Linq.Expressions.Expression.Constant(this); this.Provider = new DummyQueryProvider(); } public IEnumerator<Product> GetEnumerator() { return _products.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } // A dummy query provider for demonstration public class DummyQueryProvider : IQueryProvider { public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression) { throw new NotImplementedException(); } public object Execute(System.Linq.Expressions.Expression expression) { throw new NotImplementedException(); } } public class Program { public static void Main(string[] args) { IQueryable<Product> products = new InMemoryProductRepository(); // LINQ query - deferred execution IQueryable<Product> expensiveProductsQuery = products.Where(p => p.Price > 100m); Console.WriteLine("Executing query for products with price > 100:"); // Enumeration triggers query execution foreach (var product in expensiveProductsQuery) { Console.WriteLine($"- {product.Name} (${product.Price})"); } // Using ToList() also triggers execution var cheapProducts = products.Where(p => p.Price < 50m).ToList(); Console.WriteLine($"\nFound {cheapProducts.Count} cheap products."); } }