我在使用Linq实现一些分页时遇到了一些麻烦,我已经在这里阅读了各种问题(例如this和this),但我仍然收到错误;
System.InvalidOperationException:查询结果不能多次枚举。
我的(稍微混淆)代码是;
public List<Thing> GetThings(ObjectParameter[] params, int count, int pageIndex)
{
var things = from t in Context.ExecuteFunction<Something>("function", params)
select new Thing
{
ID = t.ID
});
var pagedThings = things;
if (pageIndex == 0)
pagedThings = things.Take(count);
else if (pageIndex > 0)
pagedThings = things.Skip(count * pageIndex).Take(count);
var countOfThings = things.Count();
return pagedThings.ToList();
}
一旦调用了最终的.ToList()
,就会抛出错误,但我看不清楚原因是什么?things.Count()
和pagedThings.ToList()
的调用是否枚举相同的内容?
编辑:我正在使用实体框架,如果这有任何区别
答案 0 :(得分:1)
如果我没弄错的话,ExecuteFunction实际上会返回一个ObjectResult,这更复杂。如果你使函数可组合(你可以在Count()时执行一个单独的查询),你可能会得到不同的结果,但是我已经有一段时间了,因为我使用低级EF,所以我不是100%肯定会工作。< / p>
由于你不能完全执行有效的两个查询,最安全的选择是创建一个完全独立的计数 - 并且通过完全独立我的意思是一个单独的函数或存储过程,它只是计数,否则你可能最终(取决于你的功能)将行返回到EF并在内存中计算它们。或者,如果可能的话,将该函数重写为视图,这可能会使其更直接。
答案 1 :(得分:0)
您正在设置pagedThings =的东西。所以你正在研究同一个对象。如果你想做你上面尝试的事情,你需要将东西复制到一个新的集合,但我建议一般重构这个代码。
您可以查看此SO帖子,了解如何在不枚举列表的情况下获取计数: How to COUNT rows within EntityFramework without loading contents?
答案 2 :(得分:0)
一般来说,Linq能够做到这一点。在LinqPad中,我编写了以下代码并成功执行了它:
void Main()
{
var sampleList = new List<int>();
for (int i = 0; i < 100; i++){
sampleList.Add(i);
}
var furtherQuery = sampleList.Take(3).Skip(4);
var count = furtherQuery.Count();
var cache = furtherQuery.ToList();
}
注意,正如您的错误所提到的,这将执行两次查询。一次用于Count(),一次用于ToList()。
您所代表的Context.ExecuteFunction<Something>("function", params)
的Linq提供商必须保护您不要进行多次昂贵的通话。您应该寻找一种只迭代结果一次的方法。例如,如上所述,您可以在已生成的List上使用.Count()。
答案 3 :(得分:0)
通常,我们称之为 pageIndex 和 pageSize 。
请检查 pageIndex ,根据您的要求,0是起始索引还是1作为起始索引。
public List<Thing> GetThings(ObjectParameter[] params, int pageIndex, int pageSize)
{
if (pageSize <= 0)
pageSize = 1;
if (pageIndex < 0)
pageIndex = 0;
var source = Context.ExecuteFunction<Something>("function", params);
var total = source.Count();
var things = (from t in source select new Thing { ID = t.ID })
.Skip(pageIndex * pageSize).Take(pageSize).ToList();
return things.ToList();
}
答案 4 :(得分:0)
这是我的代码实现。一些注意事项。 你可以在一个声明中处理Skip。 2.主要方法显示如何将多个页面传递给方法。
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<Thing> thingList = new List<Thing>();
for (int i = 0; i < 99; i++)
{
thingList.Add(new Thing(i));
}
int count = 20;
int pageIndex = 0;
int numberPages = (int)Math.Ceiling(thingList.Count * 1.0/ (count ));
for( ; pageIndex < numberPages; pageIndex ++)
{
var myPagedThings = GetThings(thingList, count, pageIndex);
foreach( var item in myPagedThings)
{
Console.WriteLine(item.ID );
}
}
}
public static IEnumerable<Thing> GetThings(List<Thing> myList, int count, int pageIndex)
{
var things = (
from t in myList
select new Thing{ID = t.ID}).ToList();
return things.Skip(count * pageIndex).Take(count);
}
}
public class Thing
{
public int ID
{ get; set; }
public Thing (){}
public Thing(int id)
{ this.ID = id; }
}
答案 5 :(得分:0)
碰巧,ExecuteFunction
导致枚举立即发生,最终意味着代码可以重新排序,并且不需要复制列表 - 现在看起来如下所示
public ThingObjects GetThings(ObjectParameter[] params, int count, int pageIndex)
{
var things = from t in Context.ExecuteFunction<Something>("function", params)
select new Thing
{
ID = t.ID
}).ToList();
var countOfThings = things.Count;
if (pageIndex >= 0)
things = things.Skip(count * pageIndex).Take(count);
return new ThingObjects(things, countOfThings);
}