С EF6 выражение:
var oldEnoughPeople = context.People.Where(p => isOldEnough(p));
, где isOldEnough
- метод в вашей кодовой базе, выдаст исключение, что EF не может преобразовать isOldEnough
в SQL. Простой обходной путь к этому заключался в том, чтобы набрать ToList()
, чтобы заставить EF вычислить выражение в Linq2Object:
var oldEnoughPeople = context.People.ToList().Where(p => isOldEnough(p));
Непосредственная проблема заключается в том, что EF будет извлекать все люди из базы данных в память на вашем сервере приложений перед применением фильтра. Это может быть очень дорого, особенно в веб-приложении, где тестирование с 1 пользовательским сеансом не выявляет проблемы, но в производственном процессе, когда поступают сотни запросов, все останавливается.
Если isOldEnough()
просто делал логику, как если сравнивать Person.Age> = 18, а затем перемещать это в предложение Where:
var oldEnoughPeople = context.People.Where(p => p.Age >= 18);
Это позволяет EF переводить выражение в SQL и передавать его в базу данных. Единственными возвращаемыми данными будут соответствующие записи о персонале.
Теперь, когда дело доходит до EF Core, разработчики установили одну черту опасной наземной мины (IMHO) в том, что, когда EF встречает то оригинальное выражение, что ононе может перевести, он будет автоматически вставлять .ToList()
, а не выбрасывать исключение. Я полагаю, что это вызывает предупреждение о том, что он это сделал, но в противном случае это может быть довольно тихой ловушкой производительности, о которой вы должны знать. Однако, по крайней мере в EF Core, экономия заключается в том, что любые выражения запроса, которые он может преобразовать в SQL, будут в первую очередь применяться к SQL.
Например, с учетом следующего выражения:
var oldEnoughPeopleStartingWithS = context.People.Where(p => p.Name.StartsWith("s") && isOldEnough(p));
EF Core выполнит запрос, чтобы выбрать всех людей, чье имя начинается с "s", поскольку EF может перевести string.StartsWith
в SQL. Эти сущности будут материализованы, и с этого момента будет применена проверка isOldEnough в памяти. Тем не менее, это должно быть тщательно продумано, потому что, если бы это была операция ИЛИ (p.Name.StartsWith("s") || isOldEnough(p)
), EF эффективно выбрал бы все записей о персонале перед фильтрацией, как в предыдущем примере .ToList()
.
При разработке с EF я настоятельно рекомендую использовать профилировщик для вашей базы данных, чтобы точно проверить, какие операторы SQL выполняются, а также влияние этих запросов на производительность и объем возвращаемых данных.