Недавно я изучал производительность своего веб-приложения и обнаружил несколько запросов LINQ, которые, я не уверен, улучшат производительность при их изменении.
В основном текущий код выглядит так:
var result = _carsRepository.GetAll()
.Where(x => x.Name == input.Name)
.FirstOrDefault();
if (result != null)
{
throw new Exception("test");
}
Думаю поменять его на:
var result = _carsRepository.GetAll()
.Where(x => x.Name == input.Name)
.Any();
if (result)
{
throw new Exception("test");
}
Насколько я понимаю, первый запрос вернет реальный объект, который мне не нужен, потому что я хочу только знать, существует ли уже запись с таким же именем в базе данных. Второй запрос возвращает только логическое значение.
Буду благодарен за любые комментарии.
Обновлено: я мог бы запустить запрос в контексте EF db, поэтому, пожалуйста, не обращайте на это внимания. Текущий репозиторий - это общий Abp.Domian.Repository. GetAll () возвращает IQueryable
Вы можете получить 100000000 записей обратно из базы данных, прежде чем делать where
, если нет, вам, вероятно, нужно проиндексировать Name
@Saruman: index or not on Name не имеет отношения к этому вопросу, потому что Where
точно такой же в обеих очередях.
@TimSchmelter, вы правы, однако я просто догадываюсь, прежде чем я перейду к следующему вопросу
Если это явно не определено как узкое место (путем измерения), производительность редко является хорошей причиной для изменения кода. Лучшая причина для изменения кода в этом случае заключается в том, что версия, использующая .Any()
, более четко выражает намерение того, что вы делаете.
Общее правило: если вам нужна только логическая обратная связь, используйте функцию Linq, которая делает именно это. Может быть, это не сильно повлияет на производительность, но это более ясно показывает ваши намерения.
Большое спасибо за ваши комментарии. Они очень полезны. Мне просто интересно, почему кто-то отклонил мой вопрос? Что я сделал не так? Я просто пытаюсь учиться у более опытных разработчиков.
Нет ничего особенного жужжание. Однако вы могли бы легко попробовать это сами.
Да, я мог бы попробовать это сам, но я задал этот вопрос, потому что знал, что получу нечто большее, чем простой ответ. @jeroenh подчеркнул, что использование Any () имеет больше смысла с точки зрения того, чего я хочу достичь.
Что возвращает GetAll()
? И не говорите «все записи», укажите Тип.
Я только что редактировал вопрос. Он возвращает IQueryable <TEntity>
@ Грентли, этот код в цикле? это может быть быстрее, если это так.
Вам не нужны все машины, вам не нужен список названий машин, чтобы увидеть, соответствует ли одна из них. Вам не нужно получать весь объект, если вам не нужно только одно поле. Итак, я выберу наиболее читаемый _carsRepository.GetAll().Any(x => x.Name == input.Name)
. Таким образом, из базы данных не будет возвращаться ни один объект, только логическое значение.
И Exception должно быть исключительным.
Мы не можем сказать вам, потому что мы не знаем, что делает ваш класс репозитория, и мы не знаем, как ваш драйвер базы данных обрабатывает преобразование в SQL.
Протестируйте и то и другое, сравните их, посмотрите SQL и проверьте с помощью инструментов анализа базы данных, если вам не хватает индекса или другой возможности оптимизации.
Только по Linq сказать невозможно.
Это действительно отвечать? Похоже на комментарий для меня. Вопрос, как вы правильно заметили, без ответа.
@HimBromBeere На мой взгляд, сообщая кому-то Почему, что это невозможно, жестяная банка может быть ответом, особенно если он содержит альтернативные шаги вперед. Но не стесняйтесь, пусть решают рецензенты.
Я должен был добавить несколько подробностей в свой вопрос. Мой вопрос очень общий, я мог бы делать LINQ в контексте EF db. Я просто хотел узнать, насколько велика разница.
Может быть небольшая разница из-за
.FirstOrDefault()
- читает все столбцы
.Any()
- Просто проверьте, есть ли записи
Разница будет в основном основана на размере данных и структуре SQL, индексах и всем остальном. Рекомендуем протестировать их путем тестирования
То есть вы говорите, что если бы таблица содержала сотни столбцов, разница была бы весьма значительной?
Это зависит от столбцов таблицы и данных. например Столбец VARCHAR (MAX) занимает значительное время по сравнению с VARCHAR (20). Есть много других факторов. Я просто отвечаю на разницу из-за First () и Any ()
Нет. Вы можете не получить никакой разницы в производительности. потому что
1) Любой() вернется, как только найдет совпадение.
2) Итерация FirstOrDefault () (вероятно) останавливается, когда находит элемент, удовлетворяющий условию.
LINQ к объектам:Enumerable.Any и Enumerable.FirstOrDefault должны работать одинаково, потому что их код почти идентичен:
FirstOr По умолчанию:
foreach (TSource source1 in source)
{
if (predicate(source1))
return source1;
}
return default (TSource);
Любой:
foreach (TSource source1 in source)
{
if (predicate(source1))
return true
}
return false;
Теперь. похоже, что вы получаете все записи из базы данных в памяти, а затем применяете предложение where.
старайтесь избегать загрузки данных в память за раз. тогда это даст вам разницу в производительности
Что делать, если один из столбцов представляет собой массив байтов размером 10 МБ
это может быть то же самое и в этом случае
Вопрос не в linq2objects. Таким образом, в случае FirstOrDefault будет сгенерирован select top 1 *
, а в случае массива байтов 10 МБ в качестве одного из столбцов, которые будут извлечены. Для Any()
это не так.
Как указывалось ранее, неясно, что делает ваш репозиторий. Однако, если вы следуете RepositoryPattern, вам следует рассмотреть возможность добавления Any в качестве метода в свой репозиторий.
public virtual bool Any(Expression<Func<T, bool>> predicate)
{
return _context.Set<T>().Any(predicate);
}
Это обеспечит выполнение Any в базе данных, поскольку этот метод выполняет Any on / как IQueryable.
Если вы не используете Generics в своем репозитории, замените T своим целевым классом.
Я не уверен, что делает GetAll()
. Если он перемещает все элементы из вашей базы данных в вашу локальную память, я бы не стал беспокоиться: попробуйте улучшить это утверждение. Зачем забирать все предметы, если вам нужен только первый.
Если GetAll()
возвращает IQueryable<...>
, то есть небольшая разница:
FirstOrDefault()
изменит выражение в запросе, так что оператор SQL будет Select top 1 ... from
.
После изменения выражения он попросит Provider
из IQqueryable
выполнить Expression
, полный результат оператора SQL будет перенесен в локальную память, которая в данном случае будет одним элементом.
Any()
будет делать почти то же самое, за исключением того, что SQL будет: Select top 1 1 from ...
Легко видеть, что Select top 1 1
передаст максимально одно целое число, а Select top 1
передаст все выбранные столбцы.
Следовательно, если вы хотите только проверить, есть ли какие-либо элементы, Any()
более эффективен, чем FirstOrDefault
.
Общее эмпирическое правило: протестируйте оба, получите тест. избегайте преждевременной оптимизации, будьте осторожны, только если это было определено как узкое место в производительности приложения.