Какая сигнатура метода Where при работе с IQueryable в LINQ?

«Какая сигнатура метода Where при работе с IQueryable в LINQ?» — вопрос из категории Entity Framework, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Сигнатура метода расширения Where для IQueryable<T> отличается от такового для IEnumerable<T> типом параметра predicate.

// Сигнатура для IQueryable<T>
public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    System.Linq.Expressions.Expression<Func<TSource, bool>> predicate // Ключевое отличие!
)

Ключевое отличие: Второй параметр — это дерево выражений (Expression), а не просто делегат (Func<TSource, bool>). Это позволяет провайдерам LINQ (как EF Core) анализировать логику предиката и транслировать ее в целевой язык запросов (например, SQL).

Пример использования с Entity Framework Core:

using (var context = new AppDbContext())
{
    // Запрос строится как Expression и преобразуется в SQL
    IQueryable<User> adultUsersQuery = context.Users
        .Where(u => u.Age >= 18 && u.IsActive); // u => ... компилируется в Expression<Func<User, bool>>

    // SQL не выполняется до материализации (ToList, FirstOrDefault и т.д.)
    List<User> adultUsers = adultUsersQuery.ToList();
}

Сравнение с IEnumerable.Where:

// Для IEnumerable<T> - предикат как делегат, фильтрация происходит в памяти
public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate // Простой делегат
);

List<User> usersList = GetUsersFromMemory();
// Фильтрация происходит в памяти .NET после загрузки всех данных
var filteredUsers = usersList.Where(u => u.Age >= 18).ToList();

Практический вывод: Использование IQueryable<T>.Where с Expression позволяет выполнять фильтрацию на стороне базы данных, что критически важно для производительности при работе с большими объемами данных.