0

我在使用 Linq 实现一些分页时遇到了一些麻烦,我已经阅读了各种问题(例如thisthis),但我仍然遇到错误;

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()调用 final ,就会抛出错误,但我不明白为什么 - 调用things.Count()pagedThings.ToList()枚举相同的东西吗?

编辑:如果这有什么不同,我正在使用实体框架

4

6 回答 6

1

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

由于您无法摆脱实际上是两个查询的执行,因此最安全的选择是制作一个完全独立的用于计数的查询 - 完全独立的意思是一个单独的函数或存储过程,它只是进行计数,否则您可能最终(取决于您的函数)将行返回到 EF 并在内存中计算它们。或者如果可能的话,将函数重写为视图,这可能会使它更直接。

于 2017-03-31T00:11:15.883 回答
0

通常,我们称它们为pageIndexpageSize

请根据您的要求检查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();
}
于 2017-03-31T00:16:15.340 回答
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()。

一定是您所代表的 Linq 提供程序Context.ExecuteFunction<Something>("function", params)正在保护您免于进行多次昂贵的调用。您应该寻找一种仅对结果进行一次迭代的方法。例如,如所写,您可以在已经生成的 List 上使用 .Count()。

于 2017-03-31T00:04:34.290 回答
0

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

您可以查看此 SO 帖子以获取有关如何在不枚举列表的情况下获取计数的一些想法: How to COUNT rows within EntityFramework without loading contents?

于 2017-03-30T23:48:55.570 回答
0

这是我对您的代码的实现。有几点需要注意。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;   }
}
于 2017-03-31T02:54:15.890 回答
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);
}
于 2017-04-05T12:10:44.283 回答