Учетная база Core Framework не имеет оптимальной производительности

Мне нужно получить количество записей с определенным фильтром.

Теоретически эта инструкция:

_dbContext.People.Count (w => w.Type == 1);

Он должен генерировать SQL как:

Select count (*)
from People
Where Type = 1

Однако сгенерированный SQL:

Select Id, Name, Type, DateCreated, DateLastUpdate, Address
from People
Where Type = 1

Создаваемый запрос занимает гораздо больше времени для работы в базе данных со многими записями.

Мне нужно сгенерировать первый запрос.

Если я просто сделаю это:

_dbContext.People.Count ();

Entity Framework генерирует следующий запрос:

Select count (*)
from People

.. который работает очень быстро.

Как сгенерировать этот второй запрос, передающий критерии поиска в счет?

Ответ 1

Здесь не так много ответов. Если ваш ORM-инструмент не дает ожидаемого SQL-запроса из простого запроса LINQ, вы не можете позволить ему это сделать, переписав запрос (и вы не должны этого делать в первую очередь).

EF Core имеет концепцию смешанной оценки клиент/база данных в запросах LINQ, которая позволяет им выпускать версии EF Core с неполной/очень неэффективной обработкой запросов, как в вашем случае.

Выдержка из Особенности не в EF Core (обратите внимание на слово не) и Roadmap:

Улучшенный перевод, позволяющий больше запросов успешно выполнять, причем больше логики оценивается в базе данных (а не в памяти).

В скором времени они планируют улучшить обработку запросов, но мы не знаем, когда это произойдет и какой уровень степени (помните, что смешанный режим позволяет им рассматривать запрос "работает" ).

Итак, каковы варианты?

  • Во-первых, держитесь подальше от EF Core, пока он не станет действительно полезным. Вернитесь к EF6, у него нет таких проблем.
  • Если вы не можете использовать EF6, то обновляйтесь с последней версией EF Core.

Например, как в версиях v1.0.1, так и в v1.1.0 вы создаете запрошенный SQL (проверенный), поэтому вы можете просто обновить его, и конкретные проблемы будут удалены.

Но обратите внимание, что наряду с улучшениями в новых выпусках появляются ошибки/регрессии (как вы можете видеть здесь EFCore, возвращающий слишком много столбцов для простого соединения LEFT OUTER), так что сделайте это на свой страх и риск (и рассмотрите первый вариант снова, то есть Какой из них подходит для вас:)

Ответ 2

Попробуйте использовать это лямбда-выражение для выполнения запроса быстрее.

_dbContext.People.select(x=> x.id).Count();

Ответ 3

Попробуйте это

(from x in _dbContext.People where x.Type == 1 select x).Count();

или вы можете сделать асинхронную версию, например:

await (from x in _dbContext.People where x.Type == 1 select x).CountAsync();

и если это не сработает для вас, вы могли бы по крайней мере сделать запрос более эффективным:

(from x in _dbContext.People where x.Type == 1 select x.Id).Count();

или

await (from x in _dbContext.People where x.Type == 1 select x.Id).CountAsync();

Ответ 4

Если вы хотите оптимизировать производительность, и нынешний поставщик EF не готов (пока), способный вызывать желаемый запрос, вы всегда можете положиться на raw SQL.

Очевидно, что это компромисс, поскольку вы используете EF, чтобы избежать прямого написания SQL, но использование необработанного SQL может быть полезно, если запрос, который вы хотите выполнить, не может быть выражен с помощью LINQ или если используется запрос LINQ приводит к неэффективному отправке SQL в базу данных.

Пример необработанного SQL-запроса будет выглядеть так:

var results = _context.People.FromSql("SELECT Id, Name, Type, " +
                                      "FROM People " +
                                      "WHERE Type = @p0",                                                     
                                      1);

Насколько мне известно, необработанные SQL-запросы, переданные методу расширения FromSql, в настоящее время требуют, чтобы вы возвращали тип модели, т.е. возвращение скалярного результата может еще не поддерживаться.

Однако вы всегда можете вернуться к простым запросам ADO.NET:

using (var connection = _context.Database.GetDbConnection())
{
    connection.Open();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT COUNT(*) FROM People WHERE Type = 1";
        var result = command.ExecuteScalar().ToString();
    }
}

Ответ 5

Получает ли это то, что вы хотите:

_dbContext.People.Where(w => w.Type == 1).Count();

Ответ 6

Кажется, что была некоторая проблема с одним из ранних выпусков Entity Framework Core. К сожалению, вы не указали точную версию, поэтому я не могу вникнуть в исходный код EF, чтобы сообщить, что именно не так.

Чтобы протестировать этот сценарий, я установил последний пакет EF Core и смог получить правильный результат.

Вот моя тестовая программа: Исходный код

И вот SQL, который создается с помощью SQL Server Profiler: введите описание изображения здесь

Как видите, он соответствует всем ожиданиям.

Вот выдержка из файла packages.config:

...
<package id="Microsoft.EntityFrameworkCore" version="1.1.0" targetFramework="net452" />
...

Итак, в вашей ситуации единственным решением является обновление до последнего пакета, который составляет 1.1.0 на момент написания этого сообщения.

Ответ 7

Я использую здесь EFCore 1.1.

Это может произойти, если EFCore не может перевести все предложение Where в SQL. Это может быть чем-то простым, как DateTime.Now, о котором может даже не думать.

В следующем выражении SQL-запрос неожиданно запускает SELECT *, а затем С# .Count() после загрузки всей таблицы!

   int sentCount = ctx.ScheduledEmail.Where(x => x.template == template &&
                   x.SendConfirmedDate > DateTime.Now.AddDays(-7)).Count();

Но этот запрос будет запускать SQL SELECT COUNT(*), как вы ожидаете/надеетесь на:

   DateTime earliestDate = DateTime.Now.AddDays(-7);
   int sentCount = ctx.ScheduledEmail.Where(x => x.template == template 
                   && x.SendConfirmedDate > earliestDate).Count();

Сумасшедший, но истинный. К счастью, это также работает:

   DateTime now = DateTime.Now;
   int sentCount = ctx.ScheduledEmail.Where(x => x.template == template &&
                   x.SendConfirmedDate > now.AddDays(-7)).Count();

Ответ 8

То, что я использовал для подсчета строк с помощью поискового запроса, было

_dbContext.People.Where(w => w.Type == 1).Count();

Это также может быть достигнуто с помощью

List<People> people = new List<People>();
people = _dbContext.People.Where(w => w.Type == 1);
int count = people.Count();

Таким образом, вы также получите список людей, если вам это нужно.