Я использую 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 (в этом случае будет возвращен весь набор), вызывает ли этот метод какие-либо проблемы с производительностью?
Похоже, вы пытаетесь реализовать общий репозиторий антиpattern поверх ORM более высокого уровня, например Entity Framework. Проверьте DbSet<T>. Что предлагает ваш собственный класс, чего нет в DbSet? Если ваш класс — это просто тонкая обертка, Зачем вы его вообще используете? Сущности не имеют внешних ключей, у них есть отношения и свойства навигации. Если ваш «репозиторий» этого не понимает, если он не может воспользоваться отношениями, потому что он «общий», он причиняет вред без реальной пользы. Это опускает уровень абстракции ниже предлагаемого EF или любой другой ORM.
Спасибо за ответы! Я вижу, что мой плохой пример вызвал некоторую путаницу, поэтому, пожалуйста, проверьте мое обновление!
Нет никакой путаницы. Улавливающие запросы просто плохи. Да, нулевые значения вызывают проблемы с производительностью, поскольку они могут привести к кэшированию и повторному использованию неправильного плана выполнения. Джесси показывает, что они вам вообще не нужны при использовании LINQ. В этой статье объясняется почему универсальные запросы плохи





Я не знаю о производительности, но вы можете обойти этот вопрос вместе, создав запрос перед выполнением. Я думаю, что это улучшает читаемость кода и уменьшает путаницу.
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%, например, тот, который сканирует таблицу вместо использования поиска по индексу.
Я ожидаю, что это не будет работать с простым var, потому что context.Set<> и .Where() имеют разные типы результатов. Наверное IQueryable<TEntity> query = ... сработает.
Я полностью согласен с тем, что это было бы лучшим решением для моего примера, но мои первые вопросы остаются в силе. Я также объяснил, почему я буду использовать его в моем обновлении вопроса.
@DánielTarsoly Думаю, комментарии Панайотиса Канаваса отвечают на этот вопрос.
Если вы знать, что
fkeyравно нулю, зачем использовать его вообще? Просто не добавляйте его к выражению. Что касается производительности, то она зависит от SQL-запрос и сгенерированного плана выполнения. Первые два запроса, вероятно, создадут один и тот же SQL или эквивалентные планы выполнения. Третий не будет, он создаст всеобъемлющий запрос, который часто приводит к плохим планам выполнения.