Подтвердить что ты не робот

Entity Framework медленно, но тот же SQL в SqlQuery быстро

Я вижу какой-то действительно странный perf, связанный с очень простым запросом с использованием Entity Framework Code-First с .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. Когда я изменяю код С# для использования функции SqlQuery, он запускается через 5-10 миллисекунд:

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

Таким образом, точно такой же SQL, результирующие объекты отслеживаются с изменениями в обоих случаях, но разница между двумя этими двумя. Что дает?

4b9b3361

Ответ 1

Нашел. Оказывается, это проблема типов данных 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; }

    ...
}

Ответ 2

Причиной замедления моих запросов, сделанных в 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.

Ответ 4

У меня была такая же проблема (запрос выполняется быстро, когда выполняется из диспетчера SQL), но при выполнении из EF истекает время ожидания.

Оказывается, что сущность (которая была создана из представления) имела неправильные сущности. Таким образом, у сущности были повторяющиеся строки с теми же ключами, и я предполагаю, что это должно было быть группировка на фоне.

Ответ 5

Я также встретил это сложным ef-запросом. Одно исправление для меня, которое уменьшило 6-секундный ef-запрос к второму запросу sql-запроса, который он сгенерировал, заключалось в том, чтобы отключить ленивую загрузку.

Чтобы найти этот параметр (ef 6), перейдите в файл .edmx и посмотрите в свойствах → Генерация кода → Lazy Loading Enabled. Установите значение false.

Массовое улучшение производительности для меня.

Ответ 6

Вы можете использовать следующие трюки, чтобы закрепить свои запросы -

  • Установите ctx.Configuration.ProxyCreationEnabled в false прямо перед тем, как вы получите контекст.
  • Кроме того, .Select(c => new {c.someproperty}) будет извлекать только требуемые данные, а не всю группу.

Сообщите мне, помогло ли это.

Ответ 7

У меня тоже была эта проблема. Оказывается, в моем случае виновником был sniffing параметра SQL-Server. Подробнее.

Первая подсказка, что моя проблема была на самом деле из-за параметра sniffing, заключалась в том, что выполнение запроса с "set arithabort off" или "set arithabort on" приводило к значительному разному времени выполнения в Management Studio. Это связано с тем, что по умолчанию ADO.NET использует "set arithabort off", а по умолчанию для Management Studio - "set arithabort on", а кеш-план запроса выполняет разные планы в зависимости от этого параметра.

Я отключил кэширование плана запроса для запроса, с помощью решения вы можете найти здесь.