Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Как правило, Entity Framework Core пытается максимально оценить запрос на сервере. EF Core преобразует части запроса в параметры, которые можно оценить на стороне клиента. Остальная часть запроса (вместе с созданными параметрами) предоставляется поставщику базы данных, чтобы определить эквивалентный запрос базы данных для вычисления на сервере. EF Core поддерживает частичное клиентское вычисление в проекции верхнего уровня (по сути, последний вызов Select()
). Если проекция верхнего уровня в запросе не может быть преобразована на сервер, EF Core будет получать все необходимые данные с сервера и оценивать оставшиеся части запроса на клиенте. Если EF Core обнаруживает выражение в любом месте, кроме представления верхнего уровня, которое не может быть преобразовано на сервере, то он вызывает исключение во время выполнения. Узнайте , как работают запросы , чтобы понять, как EF Core определяет, что нельзя перевести на сервер.
Замечание
До версии 3.0 Entity Framework Core поддерживает оценку клиента в любом месте запроса. Дополнительные сведения см. в разделе предыдущих версий.
Подсказка
Вы можете скачать используемый в этой статье пример из репозитория GitHub.
Оценка клиента в верхнеуровневой проекции
В следующем примере вспомогательный метод используется для стандартизации URL-адресов для блогов, возвращаемых из базы данных SQL Server. Так как поставщик SQL Server не имеет сведений о реализации этого метода, его невозможно преобразовать в SQL. Все остальные аспекты запроса оцениваются в базе данных, но передача возвращенных URL
с помощью этого метода выполняется на клиенте.
var blogs = await context.Blogs
.OrderByDescending(blog => blog.Rating)
.Select(
blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
.ToListAsync();
public static string StandardizeUrl(string url)
{
url = url.ToLower();
if (!url.StartsWith("http://"))
{
url = string.Concat("http://", url);
}
return url;
}
Неподдерживаемая оценка клиента
Хотя оценка клиента полезна, иногда это может привести к снижению производительности. Рассмотрим следующий запрос, в котором вспомогательный метод теперь используется в фильтре. Так как фильтр нельзя применить в базе данных, все данные должны быть извлечены в память, чтобы применить фильтр к клиенту. На основе фильтра и объема данных на сервере оценка клиента может привести к низкой производительности. Поэтому Entity Framework Core блокирует такую оценку клиента и создает исключение среды выполнения.
var blogs = await context.Blogs
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToListAsync();
Явная оценка клиента
В некоторых случаях, как показано ниже, может потребоваться принудительно выполнить оценку клиента.
- Объем данных невелик, чтобы оценка на клиенте не приводила к значительному снижению производительности.
- Используемый оператор LINQ не имеет перевода на стороне сервера.
В таких случаях вы можете явно включить оценку на стороне клиента, вызвав такие методы, как AsEnumerable
или ToList
(методы AsAsyncEnumerable
или ToListAsync
для асинхронного режима). Использование AsEnumerable
позволяет передавать результаты в потоковом режиме, тогда как использование ToList
приведет к буферизации, создавая список, что также потребляет дополнительную память. Хотя если вы перечисляете несколько раз, то хранение результатов в списке помогает больше, так как существует только один запрос к базе данных. В зависимости от конкретного использования следует оценить, какой метод является более полезным для этого случая.
var blogs = context.Blogs
.AsAsyncEnumerable()
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToListAsync();
Подсказка
Если вы используете AsAsyncEnumerable
и хотите создать запрос дальше на стороне клиента, можно использовать библиотеку System.Interactive.Async , которая определяет операторы для асинхронных перечислений. Для получения дополнительной информации см. раздел операторы LINQ на стороне клиента.
Потенциальная утечка памяти в оценке клиента
Так как перевод запросов и компиляция являются дорогостоящими, EF Core кэширует скомпилированный план запроса. Кэшированный делегат может использовать клиентский код при оценке проекции верхнего уровня. EF Core создает параметры для вычисляемых клиентом частей дерева и повторно использует план запроса, заменив значения параметров. Но некоторые константы в дереве выражений не могут быть преобразованы в параметры. Если кэшированный делегат содержит такие константы, эти объекты не могут быть собраны в мусор, поскольку на них все еще есть ссылки. Если такой объект содержит DbContext или другие службы в нем, это может привести к росту использования памяти приложения с течением времени. Это поведение обычно является признаком утечки памяти. EF Core создает исключение при каждом возникновении констант типа, который не может быть сопоставлен с использованием текущего поставщика базы данных. Распространенные причины и их решения приведены следующим образом:
- Использование метода экземпляра: при использовании методов экземпляра в клиентской проекции дерево выражений содержит константу экземпляра. Если метод не использует данные из экземпляра, рассмотрите возможность статического метода. Если вам нужны данные экземпляра в теле метода, передайте конкретные данные в качестве аргумента методу.
-
Передача константных аргументов методу: этот случай обычно возникает при использовании
this
аргумента в методе клиента. Рассмотрите возможность разделить аргумент на несколько скалярных аргументов, которые могут быть сопоставлены поставщиком базы данных. - Другие константы: если константа возникает в любом другом случае, можно оценить, требуется ли константа в обработке. Если необходимо иметь константу или если вы не можете использовать решение из приведенных выше случаев, создайте локальную переменную для хранения значения и использования локальной переменной в запросе. EF Core преобразует локальную переменную в параметр.
Предыдущие версии
Следующий раздел относится к версиям EF Core до версии 3.0.
Более старые версии EF Core поддерживают оценку клиента в любой части запроса, а не только проекцию верхнего уровня. Именно поэтому запросы, аналогичные одному, размещённого в разделе Неподдерживаемая оценка клиента, работали правильно. Поскольку это поведение может вызвать незамеченные проблемы с производительностью, EF Core зарегистрировал предупреждение о клиентской оценке. Дополнительные сведения о просмотре выходных данных ведения журнала см. в разделе "Ведение журнала".
EF Core позволяет вам изменить поведение по умолчанию таким образом, чтобы при выполнении клиентской оценки либо вызывать исключение, либо не предпринимать никаких действий, за исключением случаев проекции. Поведение, связанное с генерацией исключений, станет аналогичным поведению в версии 3.0. Чтобы изменить поведение, необходимо настроить предупреждения при настройке параметров контекста , как правило, в DbContext.OnConfiguring
или в Startup.cs
том случае, если вы используете ASP.NET Core.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}