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

Лучший способ запросить страницу данных и получить общее количество в инфраструктуре сущностей 4.1?

В настоящее время, когда мне нужно запустить запрос, который будет использоваться w/paging, я делаю это примерно так:

//Setup query (Typically much more complex)
var q = ctx.People.Where(p=>p.Name.StartsWith("A"));

//Get total result count prior to sorting
int total = q.Count();       

//Apply sort to query
q = q.OrderBy(p => p.Name);  

q.Select(p => new PersonResult
{
   Name = p.Name
}.Skip(skipRows).Take(pageSize).ToArray();

Это работает, но я задавался вопросом, возможно ли улучшить это, чтобы быть более эффективным при использовании linq? Я не мог придумать способ комбинировать счетчик с извлечением данных за один раз в DB без использования хранимой процедуры.

4b9b3361

Ответ 1

Следующий запрос получит результат подсчета и страницы в одной поездке в базу данных, но если вы проверите SQL в LINQPad, вы увидите, что это не очень красиво. Я могу только представить, как это будет выглядеть для более сложного запроса.

var query = ctx.People.Where (p => p.Name.StartsWith("A"));

var page = query.OrderBy (p => p.Name)
                .Select (p => new PersonResult { Name = p.Name } )          
                .Skip(skipRows).Take(pageSize)
                .GroupBy (p => new { Total = query.Count() })
                .First();

int total = page.Key.Total;
var people = page.Select(p => p);

Для простого запроса, например, вы могли бы использовать любой метод (2 поездки в базу данных или использование GroupBy для этого в 1 поездке) и не заметить большой разницы. Для чего-то сложного, я думаю, что наилучшим решением будет хранимая процедура.

Ответ 2

Ответ Jeff Ogata можно немного оптимизировать.

var results = query.OrderBy(p => p.Name)
                   .Select(p => new
                   {
                       Person = new PersonResult { Name = p.Name },
                       TotalCount = query.Count()
                   })          
                   .Skip(skipRows).Take(pageSize)
                   .ToArray(); // query is executed once, here

var totalCount = results.First().TotalCount;
var people = results.Select(r => r.Person).ToArray();

Это почти то же самое, за исключением того, что он не будет беспокоить базу данных ненужной GROUP BY. Если вы не уверены, что ваш запрос будет содержать хотя бы один результат и не хочет, чтобы он когда-либо генерировал исключение, вы можете получить totalCount следующим образом (хотя и менее чистым):

var totalCount = results.FirstOrDefault()?.TotalCount ?? 0;

Ответ 3

Я предлагаю сделать два запроса для первой страницы, один для общего счета и один для первой страницы или результатов.

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

Ответ 4

Важное примечание для людей, использующих EF Core> = 1.1.x:

В то время, когда я искал решение этой проблемы, эта страница заняла 1-е место в рейтинге Google "Общее количество страниц подкачки EF".

Проверив профилировщик SQL, я обнаружил, что EF генерирует SELECT COUNT(*) для каждой возвращаемой строки. Я устал от каждого решения, представленного на этой странице.

Это было протестировано с использованием EF Core 2.1.4 и SQL Server 2014. В конце концов мне пришлось выполнить их как два отдельных запроса, например. Что, по крайней мере для меня, не конец света.

var query = _db.Foo.AsQueryable(); // Add Where Filters Here.


var resultsTask = query.OrderBy(p => p.ID).Skip(request.Offset).Take(request.Limit).ToArrayAsync();
var countTask = query.CountAsync();

await Task.WhenAll(resultsTask, countTask);

return new Result()
{
    TotalCount = await countTask,
    Data = await resultsTask,
    Limit = request.Limit,
    Offset = request.Offset             
};

Похоже, что команда EF Core знает об этом:

https://github.com/aspnet/EntityFrameworkCore/issues/13739 https://github.com/aspnet/EntityFrameworkCore/issues/11186