使用 AsEnumerable() 对分页有啥影响?
Posted
技术标签:
【中文标题】使用 AsEnumerable() 对分页有啥影响?【英文标题】:What is the effect of using AsEnumerable() on paging?使用 AsEnumerable() 对分页有什么影响? 【发布时间】:2012-05-12 21:55:20 【问题描述】:我知道,如果您在 linq 查询中有一些函数没有映射到 sql 查询,那么您必须先调用 .AsEnumerable():
var model = _service.GetQuery
.Where(data => data.SomeFlag == true)
.Select(data => new Foo = CalculateFoo(data.Bar); );
不能由 linq to sql 执行,但通过添加 AsEnumerable() 我们可以使 .Select() 子句由 linq 执行到对象:
var model = _service.GetQuery
.Where(data => data.SomeFlag == true)
.AsEnumerable()
.Select(data => new Foo = CalculateFoo(data.Bar); );
但是如果数据集非常大怎么办 - AsEnumerable 对分页有什么影响?如果我说:
var page = model.Skip((page > 0 ? page - 1 : 0) * rows).Take(rows);
因为模型现在是 IEnumerable 而不是 IQueryable,所以当我们说 model.Skip().Take() 时,它是否必须首先从数据库中加载整个数据集才能跳过和获取? (这会破坏分页的目的)
编辑:这个问题是笼统地写的 - 这些是具体的细节:
我无法控制分页。我正在生成一个模型并将其传递给一个网格组件(在这种情况下为 DevExpress,但可以是任何网格)。发出分页命令的是网格组件。任何涉及在 AsEnumerable() 之前使用 .Skip().Take() 的解决方案在这里都是不可能的。
所以我需要能够将此模型传递给网格,同时还要确保模型使用延迟执行:
var model = _service.GetQuery
.Where(data => data.SomeFlag == true)
.Select(data => new
data.Id,
data.Customer.Code,
data.Customer.Name,
// etc, select a few other properties
Foo = CalculateFoo(data.Bar);
);
所以现在我遇到了一个摇滚与硬地的问题:
如果我将该模型传递给网格,它会在显示当前页面时抛出异常,因为 linq to sql 无法执行 CalculateFoo() 如果我添加 AsEnumerable() 则网格可以显示当前页面,但它必须加载整个数据集(加载数千行只是为了显示其中的 200 行) 如果我将 Foo 列排除在模型之外,那么我们将再次推迟执行,但网格缺少一列【问题讨论】:
Understanding .AsEnumerable() in LINQ to SQL 的可能重复项 我读过这个问题。它没有提到分页或 AsEnumerable 对分页的影响。 AsEnumerable 有一个行为。它不关心你对它的结果做了什么。如果您了解该行为,那么您就了解您的场景中的该行为。 看完David的帖子,我觉得答案是Yes,如果表很大,AsEnumerable
之后的skip/take会对性能产生不好的影响。跨度>
【参考方案1】:
如果你做分页,你需要...一个页面,和一个总数
所以
var query= _service.GetQuery
.Where(data => data.SomeFlag == true);
ViewBag.Total = query.Count();
var model = query.Skip((page > 0 ? page - 1 : 0) * rows).Take(rows)
.AsEnumerable()
.Select(data => new Foo = CalculateFoo(data.Bar); );
因为模型现在是 IEnumerable 而不是 IQueryable,所以当我们说 model.Skip().Take() 时,它是否必须先从数据库中加载整个数据集才能跳过和获取? (这会破坏分页的目的)
这是一个事实:在“分页”之前,您总是只有“被 linq2entites 接受”查询。
编辑
我无法控制分页。我正在生成一个模型并将其传递给一个网格组件(在这种情况下为 DevExpress,但可以是任何网格)。发出分页命令的是网格组件。任何涉及在 AsEnumerable() 之前使用 .Skip().Take() 的解决方案在这里都是不可能的。
嗯,“通常”网格系统允许(telerik)(或只有(MvcContrib)自定义分页(这意味着您必须提供“选定页面”结果+总数,如我的回答)。
我在“DevExpress 自定义分页”上进行了搜索(您可以进一步搜索),但结果很少。不知道他们是否对你感兴趣。
答案示例
http://www.devexpress.com/Support/Center/p/Q264453.aspx
http://community.devexpress.com/forums/p/98848/338135.aspx
【讨论】:
不幸的是,我不是控制分页的人。我能做的就是发送一个模型。我会用详细信息更新问题 +1 感谢您的链接 - 没有回答手头的问题,但它确实让我意识到 devex 无论如何都没有正确分页 linq - 它实际上是加载所有行然后采取一页 - 我在这里试图避免的确切行为。看起来他们有一个叫做 linq 数据源的东西可以帮助解决这个问题。【参考方案2】:在AsEnumerable()
之前调用Skip/Take
:
var model = _service.GetQuery
.Where(data => data.SomeFlag == true)
.Skip((page > 0 ? page - 1 : 0) * rows).Take(rows)
.AsEnumerable()
.Select(data => new Foo = CalculateFoo(data.Bar); );
【讨论】:
我希望这么简单 :) 更新了问题以解释为什么我不能将 .Skip().Take() 放在 .AsEnumerable() 之前以上是关于使用 AsEnumerable() 对分页有啥影响?的主要内容,如果未能解决你的问题,请参考以下文章
RxJS:使用 NestJS HttpService 对分页 API 进行并行 http 调用