Как EF использует функцию обратного вызова, предоставленную в функции LINQ `Where`, для выбора элементов из БД? - PullRequest
0 голосов
/ 17 октября 2019

Как EF использует функцию обратного вызова, предоставленную в функции LINQ Where, для выбора элементов из БД?

Например, если у меня есть запрос context.People.Where(p => isOldEnough(p)), означает ли это, что EF будет запрашивать вселюди из БД, а затем применить к ним предикат и вернуть оставшийся результат, или предикат каким-то образом преобразуется в фактический запрос к БД?

Я знаю, что фактический запрос к БД произойдет, как толькорезультат context.People.Where(p => isOldEnough(p)) будет использоваться каким-либо образом (например, в итерации или приведениях).

Мне не удалось найти эту информацию в Интернете, поэтому я решил спросить ее здесь.

1 Ответ

1 голос
/ 18 октября 2019

С 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 выполняются, а также влияние этих запросов на производительность и объем возвращаемых данных.

...