更好的方法来查询数据页面并获得实体框架4.1中的总计数?

时间:2011-10-14 12:14:54

标签: entity-framework c#-4.0 entity-framework-4 entity-framework-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时提高效率更高效?我想不出一种方法可以使用存储过程将数据检索中的数据与数据检索结合到数据库中。

4 个答案:

答案 0 :(得分:74)

以下查询将在一次数据库中获取计数和页面结果,但是如果在LINQPad中检查SQL,您会发现它不是很漂亮。我只能想象一下更复杂的查询会是什么样子。

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次旅行中执行此操作)并且没有注意到太多差异。对于任何复杂的事情,我认为存储过程是最好的解决方案。

答案 1 :(得分:7)

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;

答案 2 :(得分:3)

我建议对第一页进行两次查询,一次针对总计数,一次针对第一页或结果。

在超出第一页时缓存总使用次数。

答案 3 :(得分:3)

使用EF Core> = 1.1.x的人员的重要注意事项:

当时我正在寻找解决方案,此页面在Google术语“ EF核心分页总数”中排名第1。

检查了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核心团队意识到了这一点:

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