У меня был запрос LINQ, который загружает иерархию объектов, как показано ниже.
Запрос № 1
var result = db.Orders
.Include("Customer")
// many other .Include() here
.FirstOrDefault(x => x.Customer.CustomerId == 1 &&
x.OrderId == orderId);
У меня были серьезные проблемы с производительностью.
Загрузка процессора составляла около 100%, а использование памяти было очень высоким.
И я настроил это следующим образом, и проблема с производительностью была исправлена.
Запрос № 2
var result = db.Orders
.Include("Customer")
// many other .Include() here
.Where(x => x.Customer.CustomerId == 1 &&
x.OrderId == orderId)
.FirstOrDefault();
Я просто хочу подтвердить свое подозрение.
Запрос № 1, вероятно, просматривает все мои записи в памяти в поисках подходящей записи.
против
Запрос № 2 фильтрует записи в базе данных, а затем получает только первую запись.
Поэтому Query # 1 имеет проблемы с производительностью?
Просто чтобы быть в безопасности, мне нужно использовать .Select(x => x)
перед .FirstOrDefault()
?
Запрос № 3
var result = db.Orders
.Include("Customer")
// many other .Include() here
.Where(x => x.Customer.CustomerId == 1 &&
x.OrderId == orderId)
.Select(x => x)
.FirstOrDefault();
Нет, они оба должны приводить к одному и тому же запросу SQL при выполнении. Вы можете доказать это, заглянув в SQL Profiler и посмотрев, какой именно SQL отправляется из EF в обоих случаях. Ваша оптимизация производительности должна была быть вызвана некоторыми другими факторами. Вот почему:
Включить метод возвращает ObjectQuery <T> :
public class ObjectQuery<T> : ObjectQuery, IOrderedQueryable<T>,
IQueryable<T>, IEnumerable<T>,
IOrderedQueryable, IQueryable,
IEnumerable, IListSource
Это означает, что его метод FirstOrDefault имеет 2 перегрузки:
// Defined by Enumerable:
FirstOrDefault(Func<T, Boolean>)
// Defined by Queryable:
FirstOrDefault(Expression<Func<T, Boolean>>)
Когда вы .FirstOrDefault(x => x.Customer.CustomerId == 1
компилятор перейдет в процесс с именем Overload Resolution, чтобы вывести тип лямбда-выражения x => x.Customer.CustomerId == 1
поскольку он может быть преобразован в тип обоих типов параметров перегрузки.
Компилятор будет использовать алгоритм (который я все еще пытаюсь найти в Спецификации языка C #!), Выясните, что преобразование лямбда- Expression<Func<T, Boolean>
в Expression<Func<T, Boolean>
лучше, чем в Func<T, Boolean>
поэтому выберите IQueryable перегрузка.
Следовательно, вы увидите предикат в сгенерированном SQL, когда будете наблюдать его в SQL Profiler.
Я думаю, что лучше было бы использовать ... Где ( условие ) .Take (1) .irstOrDefault (), потому что Take (1) может быть легко переведен в SQL как предложение TOP Кто-нибудь со мной?