Производительность Entity Framework DbSet<TEntity>.Where(e => true)

Я использую Entity Framework 6 для доступа к базе данных.

Есть ли разница в производительности между следующими двумя методами?

public IEnumerable<TEntity> GetAll()
{
    using (var context = new DbContext())
        return context.Set<TEntity>().ToList();
}

а также

public IEnumerable<TEntity> GetAll()
{
    using (var context = new DbContext())
        return context.Set<TEntity>().Where(e => true).ToList();
}

Я спрашиваю, потому что я хотел бы использовать условные предикаты. Примерно так следующее.

public IEnumerable<TEntity> GetAll(TKey fKey)
{
    using (var context = new DbContext())
        return context.Set<TEntity>()
                      .Where(e => fKey != null ? e.fKey == fKey : true).ToList();
}

где fKey будет внешним ключом.

Обновлять: Поскольку многие комментарии сосредоточены на моем последнем примере, который, я согласен, является плохим вариантом использования, я объясню, почему я спрашиваю.

Итак, у меня есть метод репозитория, который выглядит примерно так:

public class EntityRepository
{
    public IEnumerable<Entity> GetAll(Expression<Func<Entity, bool>> predicate)
    {
        using (var context = new DbContext())
            return context.Set<Entity>.Where(predicate).ToList();
    }
}

И я использую этот метод из метода действия контроллера веб-API. Что-то вроде следующего.

public IHttpActionResult GetEntities(string param1, string param2, string param3)
{
    Expression<Func<Entity, bool>> predicate = e =>
         (param1 != null ? e.field1 == param1 : true)
         && (param2 != null ? e.field2 == param2 : true)
         && (param3 != null ? e.field3 == param3 : true);
    var entities = EntityRepository.GetAll(predicate);
    return Ok(entities);
}

Итак, здесь я получаю некоторые параметры запроса из URI и создаю предикат на их основе. Некоторые из этих параметров могут быть null, и в этом случае я не хочу фильтровать их. Но я не хочу создавать разные предикаты для всех комбинаций параметров, являющихся nulls или нет.

Я знаю, что могу прочитать весь набор и потом отфильтровать по одному, но это потребует много памяти с большими наборами данных.

Итак, просто чтобы уточнить мой вопрос: Это правильный подход? Если все 3 параметра равны null (в этом случае будет возвращен весь набор), вызывает ли этот метод какие-либо проблемы с производительностью?

Если вы знать, что fkey равно нулю, зачем использовать его вообще? Просто не добавляйте его к выражению. Что касается производительности, то она зависит от SQL-запрос и сгенерированного плана выполнения. Первые два запроса, вероятно, создадут один и тот же SQL или эквивалентные планы выполнения. Третий не будет, он создаст всеобъемлющий запрос, который часто приводит к плохим планам выполнения.

Panagiotis Kanavos 05.03.2019 08:52

Похоже, вы пытаетесь реализовать общий репозиторий антиpattern поверх ORM более высокого уровня, например Entity Framework. Проверьте DbSet<T>. Что предлагает ваш собственный класс, чего нет в DbSet? Если ваш класс — это просто тонкая обертка, Зачем вы его вообще используете? Сущности не имеют внешних ключей, у них есть отношения и свойства навигации. Если ваш «репозиторий» этого не понимает, если он не может воспользоваться отношениями, потому что он «общий», он причиняет вред без реальной пользы. Это опускает уровень абстракции ниже предлагаемого EF или любой другой ORM.

Panagiotis Kanavos 05.03.2019 08:57

Спасибо за ответы! Я вижу, что мой плохой пример вызвал некоторую путаницу, поэтому, пожалуйста, проверьте мое обновление!

Dániel Tarsoly 05.03.2019 10:44

Нет никакой путаницы. Улавливающие запросы просто плохи. Да, нулевые значения вызывают проблемы с производительностью, поскольку они могут привести к кэшированию и повторному использованию неправильного плана выполнения. Джесси показывает, что они вам вообще не нужны при использовании LINQ. В этой статье объясняется почему универсальные запросы плохи

Panagiotis Kanavos 05.03.2019 10:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
171
1

Ответы 1

Я не знаю о производительности, но вы можете обойти этот вопрос вместе, создав запрос перед выполнением. Я думаю, что это улучшает читаемость кода и уменьшает путаницу.

public IEnumerable<TEntity> GetAll(TKey fKey)
{
    using (var context = new DbContext())
    {
        IQueryable<TEntity> query = context.Set<TEntity>();

        if (fKey != null)
        {
            query = query.Where(e => e.fKey == fKey);
        }

        return query.ToList();
    }
}

Обновлено: Вызывая редактирование вашего вопроса, я думаю, что следующий метод будет иметь такое же использование, но избегает ненужных операторов в запросах sql:

public IEnumerable<Entity> GetAll(
    Func<IQueryable<Entity>, IQueryable<Entity>> query)
{
    using (var context = new DbContext())
        return query(context.Set<Entity>).ToList();
}

// then use like this:
EntityRepository.GetAll((entities) =>
    {
        var query = entities;
        if (param1 != null) query = query.Where(e => e.field1 == param1);
        if (param2 != null) query = query.Where(e => e.field2 == param2);
        if (param3 != null) query = query.Where(e => e.field3 == param3);
        return query;
    });

Это также повышает производительность. fKey != null ? e.fKey == fKey : true будет преобразован в SQL, генерирующий запрос, чей (кэшированный) план выполнения будет зависеть от значения fkey при первом выполнении. Вероятность того, что план выполнения неправильно будет сгенерирован, составляет 50%, например, тот, который сканирует таблицу вместо использования поиска по индексу.

Panagiotis Kanavos 05.03.2019 08:59

Я ожидаю, что это не будет работать с простым var, потому что context.Set<> и .Where() имеют разные типы результатов. Наверное IQueryable<TEntity> query = ... сработает.

Peter B 05.03.2019 09:06

Я полностью согласен с тем, что это было бы лучшим решением для моего примера, но мои первые вопросы остаются в силе. Я также объяснил, почему я буду использовать его в моем обновлении вопроса.

Dániel Tarsoly 05.03.2019 10:50

@DánielTarsoly Думаю, комментарии Панайотиса Канаваса отвечают на этот вопрос.

Jesse de Wit 05.03.2019 10:54

Другие вопросы по теме

Нужно ли нам создавать отдельные таблицы для различных типов пополнений, таких как (mobilerech, dthrech, elecbill, waterbill и т. д.), или это можно сделать в одной таблице?
Как отображать определенные поля из БД, используя выбранное значение в раскрывающемся списке в php
Как я могу сохранить объект изображения в базе данных?
Где ошибка в моем коде в ajax или в django?
Загрузка только имени изображения вместе с некоторыми данными в базу данных, и только изображение должно храниться в папке в веб-контенте
Sas-запрос для планирования заданий
Загрузка только имени изображения вместе с некоторыми данными в базе данных и изображением в папке в веб-контенте
Одна таблица или несколько таблиц для данных иерархии
Php-mysqli insert_id возвращает 0 вместо последнего идентификатора, когда вызов объекта переключается со статического вызова
Как запустить проект laravel с postgresql? (как запустить localhost/имя проекта в веб-браузере?)