Запрос Entity Framework медленный, но тот же SQL в SqlQuery выполняется быстро
Я вижу некоторые действительно странные perf, связанные с очень простым запросом с использованием кода Entity Framework-сначала с .NET framework версии 4. Запрос LINQ2Entities выглядит следующим образом:
context.MyTables.Where(m => m.SomeStringProp == stringVar);
Это занимает более 3000 миллисекунд для выполнения. Сгенерированный SQL выглядит очень просто:
SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
...
FROM [MyTable] as [Extent1]
WHERE [Extent1].[SomeStringProp] = '1234567890'
этот запрос выполняется почти мгновенно при запуске через Management Studio. Когда я изменяю код C# для использования функции SqlQuery, он работает в 5-10 миллисекунды:
context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);
Итак, точно такой же SQL, результирующие сущности отслеживаются в обоих случаях, но дикая разница perf между ними. Что это дает?
7 ответов:
нашли его. Оказывается это проблема типов данных SQL. Элемент
SomeStringProp
столбец в базе данных был varchar, но EF предполагает, что типы строк .NET являются nvarchars. Результирующий процесс перевода во время запроса к БД для выполнения сравнения-это то, что занимает много времени. Я думаю, что EF Prof немного сбивал меня с толку здесь, более точное представление выполняемого запроса было бы следующим:SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp], ... FROM [MyTable] as [Extent1] WHERE [Extent1].[SomeStringProp] = N'1234567890'
таким образом, полученное исправление заключается в аннотировании первой модели кода, указание правильного типа данных SQL:
public class MyTable { ... [Column(TypeName="varchar")] public string SomeStringProp { get; set; } ... }
причиной замедления моих запросов, сделанных в EF, было сравнение не нулевых скаляров с нулевыми скалярами:
long? userId = 10; // nullable scalar db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value ^^^^^^^^^ ^^^^^^ Type: long Type: long?
этот запрос занял 35 секунд. Но крошечный рефакторинг вроде этого:
long? userId = 10; long userIdValue = userId.Value; // I've done that only for the presentation pursposes db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList() ^^^^^^^^^ ^^^^^^^^^^^ Type: long Type: long
дает невероятные результаты. Это заняло всего 50 мс, чтобы закончить. Вполне возможно, что это ошибка в EF.
Если вы используете свободное отображение, вы можете использовать
IsUnicode(false)
в составе конфигурации, чтобы получить тот же эффект -http://msdn.microsoft.com/en-us/data/jj591617.aspx#1.9
http://msdn.microsoft.com/en-us/library/gg696416%28v=vs.103%29.aspx
У меня была та же проблема (запрос выполняется быстро из SQL manager), но при выполнении из EF истекает тайм-аут.
оказывается, что сущность (которая была создана из представления) имела неправильные ключи сущности. Таким образом, у сущности были повторяющиеся строки с теми же ключами, и я думаю, что это должно было сделать группировку на заднем плане.
Я также наткнулся на это со сложным запросом ef. Одно исправление для меня, которое уменьшило 6-секундный запрос ef до субсекундного sql-запроса, который он сгенерировал, состояло в том, чтобы отключить ленивую загрузку.
чтобы найти эту настройку (ef 6), перейдите в раздел .edmx-файл и посмотрите в свойствах - > генерация кода - > ленивая загрузка включена. Задать значение false.
массовое улучшение производительности для меня.
вы можете использовать следующие приемы, чтобы закрепить ваши запросы -
- Set
ctx.Configuration.ProxyCreationEnabled
доfalse
прямо перед тем, как вы получите контекст.- и
.Select(c => new {c.someproperty})
будет получать только необходимые данные, а не всю кучу.Дайте мне знать, если это помогло.
У меня тоже была эта проблема. Оказывается виновником в моем случае был SQL-сервер параметр sniffing.
первая подсказка, что моя проблема была на самом деле из-за нюхания параметров, заключалась в том, что выполнение запроса с "set arithabort off" или "set arithabort on" привело к совершенно разным временам выполнения в Management Studio. Это потому что ADO.NET по умолчанию используется "set arithabort off", а среда Management Studio по умолчанию - "set arithabort on". Кэш плана запроса сохраняет разные планы в зависимости от этого параметра.
Я отключил кэширование плана запроса для запроса, с решением вы можете найти здесь.