C# 使用.Take()和.Skip()获取总计数时遇到问题

C# 使用.Take()和.Skip()获取总计数时遇到问题,c#,entity-framework,linq,skip-take,C#,Entity Framework,Linq,Skip Take,我在使用Linq实现一些分页时遇到了一些问题,我已经阅读了这里的各种问题(例如),但是我仍然得到了错误 System.InvalidOperationException:查询结果不能枚举多次 我的(稍微模糊的)代码是 public List GetThings(ObjectParameter[]params,int count,int pageIndex) { var things=来自Context.ExecuteFunction(“函数”,参数)中的t 选择新事物 { ID=t.ID });

我在使用Linq实现一些分页时遇到了一些问题,我已经阅读了这里的各种问题(例如),但是我仍然得到了错误

System.InvalidOperationException:查询结果不能枚举多次

我的(稍微模糊的)代码是

public List GetThings(ObjectParameter[]params,int count,int pageIndex)
{
var things=来自Context.ExecuteFunction(“函数”,参数)中的t
选择新事物
{
ID=t.ID
});
var pagedThings=事物;
如果(pageIndex==0)
pagedThings=事物。获取(计数);
否则如果(页面索引>0)
pagedThings=things.Skip(count*pageIndex)。Take(count);
var countOfThings=things.Count();
返回pagedThings.ToList();
}
一旦调用了最后一个
.ToList()
,就会抛出错误,但我看不出原因-对
things.Count()
pagedThings.ToList()的调用是否枚举了相同的内容


编辑:如果实体框架有任何不同,我将使用它

您正在设置pagedThings=things。因此,您正在处理同一个对象。如果您想执行上面尝试的操作,您需要将内容复制到新集合中,但我建议您总体上重构此代码

您可以查看此SO帖子,了解如何在不枚举列表的情况下获取计数:

一般来说,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();
void Main()
{
var sampleList=新列表();
对于(int i=0;i<100;i++){
样本列表。添加(i);
}
var-furthequery=sampleList.Take(3)、Skip(4);
var count=furthquery.count();
var cache=furthquery.ToList();
}

注意,正如您的错误所提到的,这将执行两次查询。一次用于Count(),一次用于ToList()


您表示为
Context.ExecuteFunction(“function”,params)
的Linq提供程序一定是在保护您避免进行多个昂贵的调用。您应该寻找一种只对结果迭代一次的方法。例如,如前所述,您可以在已经生成的列表中使用.Count()。

ExecuteFunction如果我没有弄错的话,实际上会返回一个ObjectResult,即。。。更复杂。如果使函数可组合(Count()时将执行一个单独的查询),则可能会得到不同的结果,但我使用低级别EF已经有一段时间了,所以我不能100%确定这是否可行


由于您不能不执行有效的两个查询,因此最安全的方法是使用一个完全独立的查询进行计数,我所说的完全独立是指一个单独的函数或存储过程,它只进行计数,否则您可能最终(取决于您的函数)将行返回EF并在内存中计数。或者,如果可能的话,将函数重写为视图,这可能会使它更简单。

通常,我们称它们为pageIndexpageSize

请检查页面索引根据您的要求,是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();
}
public List GetThings(ObjectParameter[]params,int pageIndex,int pageSize)
{

if(pageSize这是我对代码的实现。需要注意的几点。 1.您可以在一条语句中处理Skip。 2.main方法显示了如何将多个页面传递到该方法中

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;   }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
公共课程
{
公共静态void Main()
{
列表内容列表=新列表();
对于(int i=0;i<99;i++)
{
添加(新事物(i));
}
整数计数=20;
int pageIndex=0;
int numberPages=(int)数学上限(thingList.Count*1.0/(Count));
对于(;pageIndex
碰巧,
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);
}
public ThingObjects GetThings(ObjectParameter[]params,int count,int pageIndex)
{
var things=来自Context.ExecuteFunction(“函数”,参数)中的t
选择新事物
{
ID=t.ID
}).ToList();
var countOfThings=事物。Count;
如果(页面索引>=0)
things=things.Skip(计数*页面索引)。Take(计数);
返回新事物(事物、事物计数);
}

出于理智的考虑,顶部的
事物的类型是否类似于
IEnumerable
IQueryable
?或者类似的东西?是的-这是IEnumerablet两个独立的
Skip()背后的原因
语句绝对是以零为基础的,但这不会对列表的枚举时间产生任何影响,不是吗?啊!当涉及对象的浅/深副本时,我总是犯这个错误-你会认为我现在已经学会了!作为旁注-示例代码是对我试图做的事情的一个非常快速的模拟,而不是实际的code.+1,因为它为我指明了正确的方向。双枚举
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);
}