如何从分页的 ef 核心查询中获取可用的总行数

Posted

技术标签:

【中文标题】如何从分页的 ef 核心查询中获取可用的总行数【英文标题】:How to get total available rows from paginated ef core query 【发布时间】:2021-09-27 18:28:02 【问题描述】:

提前感谢您花时间阅读此问题。

我的数据库中有一个视图,我们称之为 Members_VW

在我的 .net 5 API 中,我试图从带有搜索参数的视图中获取成员列表的分页响应。我还需要返回响应的总数,以便前端知道结果将在多少页中返回。

目前,Members_VW 是通过如下查询生成的:

select
    col1, col2, col3
from
    table1 1
    inner join table2 2 on 1.key = 2.key
    inner join tble3 3 on 3.key = 2.key
where
    defaultcondition1 = '1'
        and
    defaultcondition2 = '2'

我参考了this 的答案并尝试使用 CTE,最终将我的观点改为使用这样的查询:

with cte1 as (
select
    col1, col2, col3
from
    table1 1
    inner join table2 2 on 1.key = 2.key
    inner join tble3 3 on 3.key = 2.key
where
    defaultcondition1 = '1'
        and
    defaultcondition2 = '2')
cte2 as (
select count(*) over() from cte1 )
select
    *
from
    cte1, cte2

但这不起作用,因为它总是返回cte1 中的总行数,而没有应用任何过滤器。

于是,我继续尝试构造查询,返回应用条件后的总行数,发现这个查询有效:

select
    col1, col2, col3, count(*) over()
from
    table1 1
    inner join table2 2 on 1.key = 2.key
    inner join tble3 3 on 3.key = 2.key
where
    defaultcondition1 = '1'
        and
    defaultcondition2 = '2'

目前,我正在尝试使用 EF Core 实现相同的查询,但正在努力实现。

我已尝试实施here 提供的解决方案,但正如其中一位 cmets 所建议的,不再允许此实施。

我试图避免使用原始查询的实现。有没有在不使用原始查询的情况下从count(*) over() 获取结果?

以下是我目前的实现:

IQueryable<MembersVW> membersQuery = _context.MembersVW;
membersQuery = membersQuery.Where(u => u.MemberId == memberid);
membersQuery = membersQuery.OrderBy(m => m.MemberId).Skip(page * size).Take(size);

当我这样做时: membersQuery = membersQuery.Count() 我返回以下错误:

Error   CS0029  Cannot implicitly convert type 'int' to 'System.Linq.IQueryable<PersonalPolicyAPI.Models.VwPersonalPolicyMember>'

再次感谢您阅读我的问题,感谢您提供的任何帮助。 ??????????

【问题讨论】:

如果您在此行之前运行计数查询会发生什么membersQuery = membersQuery.OrderBy(m =&gt; m.MemberId).Skip(page * size).Take(size); @RoarS。没有计数,它可以工作,并返回所有结果。但这不是我需要的。我需要能够从查询中分页以优化查询响应时间。 int count= membersQuery.Count()代替membersQuery = membersQuery.Count() 【参考方案1】:

membersQuery.Count() 返回整数而不是可查询的

你可以的

     int count = membersQuery.Count();
     List<MemberVW> = membersQuery.OrderBy(m => m.MemberId).Skip(page * size).Take(size).ToList();

你可以返回

       public class MemberVwWithCount 
           public int Countget;set;
           public List<MemberVW> Members get; set;
         

【讨论】:

感谢@a_k 的回复。我试过你的实现。有效。但是,它执行 2 个不同的查询。我试图通过一个查询来完成这项工作。除非您认为仅通过一个查询就无法完成? 这可以在单个查询中完成,但会降低性能。所以我认为@a_k 建议的当前方法是可取的。【参考方案2】:

您尝试将计数值(整数)分配给查询的变量,即 IQueryable。仅此而已。

如果您想在一个查询中执行此操作,正如您在其中一个 cmets 中建议的那样,您可以首先执行查询以获取所有条目,然后计算结果,然后使用跳过/获取过滤结果。这很可能不是最有效的方法,但应该可以。

如果您不修改此函数/api 中的任何数据,我还建议使用 AsNoTracking()。

编辑: 我现在建议这个解决方案。计数很快,因为它实际上不获取任何数据而只计算行数。这仍然是两个查询,我会尝试将其合并并稍后编辑我的答案。

 var count = await yourContext.YourTable.CountAsync();
 var data = await yourContext.YourTable
    .OrderBy(x => x.YourProp)
    .Skip(10).Take(10)
    //.AsNoTracking()
    .ToListAsync();

编辑2: 好的,所以,我还不能让它只在 DB-Call 上进行,但是,我可以在语法上组合它。但是,我第一次编辑中的方法更容易阅读并且基本相同。不过,要更深入地研究这一点,必须有一种时髦的方法来做到这一点。

var query = yourContext.YourTable.AsQueryable();
var result =  await query.OrderBy(x => x.Prop)
                .Select(x => new Data = x, Count = query.Count() )
                .Skip(50).Take(50)
                .AsNoTracking()
                .ToListAsync();
var count = result.FirstOrDefault()?.Count ?? 0; //If empty/null return 0
var data = result.Select(x => x.Data).ToList();

【讨论】:

感谢您的回答塞巴斯蒂安!希望你能遇到那种时髦的方式【参考方案3】:

membersQuery = membersQuery.Count() 行中,您将整数值分配给可查询列表,这是不正确的。您可以像这样在查询后获取列表项计数,即

membersQuery = membersQuery.OrderBy(m => m.MemberId).Skip(page * size).Take(size);
int totalCount = membersQuery.Count();

要在同一个列表中获取计数列,首先需要在 MembersVW 类中添加 Count 属性,然后使用 LINQ 投影来添加列值。

解决方案 1:

memberQuery = membersQuery.Select(p => new MembersVW 
              
                  col1 = p.col1
                  col2 = p.col2
                  col3 = p.col3
                  count = totalCount
              );

解决方案 2:

使用 LINQ foreach 循环,即

membersQuery.ForEach(item => 
              
                  item.count = totalCount;
              );

【讨论】:

感谢您的回答!你说得对。但它仍然会生成 2 个单独的查询。我正在寻找一种可能的解决方案,该解决方案使用具有总计数的列返回结果。 您可以使用上面的查询投影来实现它,但是,您还需要在您的 MembersVW 类中添加 Count 属性。我已经编辑了我的答案。【参考方案4】:

我已阅读您关于能否通过一个查询完成的问题。虽然我不知道用 1 个查询来做这件事的任何方法,但我可以提供另一种解决方案来帮助您解决对性能和 2 个查询的担忧。我经常这样做。 ? 试试:

//execute both queries at the same time instead of sequentially
var countqry = membersQuery.CountAsync();
var pageqry = membersQuery.OrderBy(m => m.MemberId).Skip(page * size).Take(size).ToListAsync();

//wait for them both to complete
Task.WaitAll(countqry, pageqry);

//use the results
var count = countqry.Result;
var page = pageqry.Result;

【讨论】:

以上是关于如何从分页的 ef 核心查询中获取可用的总行数的主要内容,如果未能解决你的问题,请参考以下文章

从分页的 CakePHP 控制器中找出有多少页的结果

分库分表后的分页查询

如何在 Material-UI、ReactJS 中从分页中设置分页项的样式?

表单查询和分页查询和查询排序

怎样获取gridview中数据总行数

GridView 控件中使用 LinqDataSource 和分页的总行数