如何从分页的 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 => 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 核心查询中获取可用的总行数的主要内容,如果未能解决你的问题,请参考以下文章