使用LINQ为对象分页

use*_*890 82 .net c# linq paging

如何在LINQ查询中实现分页?实际上,如果可以模仿sql TOP函数,我会感到满意.但是,我确信无论如何都需要尽快提供完整的分页支持.

var queryResult = from o in objects
                  where ...
                  select new
                      {
                         A = o.a,
                         B = o.b
                      }
                   ????????? TOP 10????????
Run Code Online (Sandbox Code Playgroud)

Dav*_*fer 210

您正在寻找SkipTake扩展方法.Skip移过结果中的前N个元素,返回余数; Take返回结果中的前N个元素,删除任何剩余的元素.

有关如何使用这些方法的详细信息,请参阅MSDN:http://msdn.microsoft.com/en-us/library/bb386988.aspx

例如:

int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
  .Skip(numberOfObjectsPerPage * pageNumber)
  .Take(numberOfObjectsPerPage);
Run Code Online (Sandbox Code Playgroud)

  • 这将导致跳过第一页IF pageNumber不是零(0).如果pageNumber以1开头,则使用此".Skip(numberOfObjectsPerPage*(pageNumber - 1))" (43认同)
  • 我是否应该使用相同的技术而不是SQL与庞大的数据库,它会先将整个表放入内存然后丢弃不需要的内容吗? (7认同)
  • 顺便说一句,如果您对幕后发生的事情感兴趣,大多数 LINQ 数据库驱动程序都提供了一种方法来获取正在执行的实际 SQL 的调试输出信息。 (2认同)

Tom*_*cek 50

使用SkipTake绝对是要走的路.如果我实现这个,我可能会编写自己的扩展方法来处理分页(使代码更具可读性).实施当然可以使用SkipTake:

static class PagingUtils {
  public static IEnumerable<T> Page<T>(this IEnumerable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
  public static IQueryable<T> Page<T>(this IQueryable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
}
Run Code Online (Sandbox Code Playgroud)

该类定义了两个扩展方法 - 一个用于IEnumerable和一个用于IQueryable,这意味着您可以将它与LINQ to Objects和LINQ to SQL一起使用(编写数据库查询时,编译器将选择IQueryable版本).

根据您的分页要求,您还可以添加一些其他行为(例如,处理负数pageSizepage值).以下是如何在查询中使用此扩展方法的示例:

var q = (from p in products
         where p.Show == true
         select new { p.Name }).Page(10, pageIndex);
Run Code Online (Sandbox Code Playgroud)

  • 我相信这将返回整个结果集,然后在内存中而不是在服务器上进行过滤.如果这是SQL,则会对数据库造成巨大的性能影响. (3认同)
  • 您当然可以轻松地为`IQueryable`添加一个重载,以使其与数据库查询一起使用(我编辑了答案并添加了它).有点不幸的是你不能以完全通用的方式编写代码(在Haskell中,这可以通过类型类来实现).最初的问题提到LINQ to Objects,所以我只写了一个重载. (2认同)

Luk*_*oid 31

这是我在使用LINQ对象时进行分页的高效方法:

public static IEnumerable<IEnumerable<T>> Page<T>(this IEnumerable<T> source, int pageSize)
{
    Contract.Requires(source != null);
    Contract.Requires(pageSize > 0);
    Contract.Ensures(Contract.Result<IEnumerable<IEnumerable<T>>>() != null);

    using (var enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            var currentPage = new List<T>(pageSize)
            {
                enumerator.Current
            };

            while (currentPage.Count < pageSize && enumerator.MoveNext())
            {
                currentPage.Add(enumerator.Current);
            }
            yield return new ReadOnlyCollection<T>(currentPage);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以这样使用:

var items = Enumerable.Range(0, 12);

foreach(var page in items.Page(3))
{
    // Do something with each page
    foreach(var item in page)
    {
        // Do something with the item in the current page       
    }
}
Run Code Online (Sandbox Code Playgroud)

该垃圾没有SkipTake这将是如果你有兴趣在多个页面非常低效.

  • 这只是被偷了,放在我的共同的库中,谢谢!我只是将方法重命名为`Paginate`以删除`noun` vs`verb`歧义. (4认同)

Noe*_*oel 8

   ( for o in objects
    where ...
    select new
   {
     A=o.a,
     B=o.b
   })
.Skip((page-1)*pageSize)
.Take(pageSize)
Run Code Online (Sandbox Code Playgroud)


Jac*_*tti 5

编辑-删除了跳过(0),因为它没有必要

var queryResult = (from o in objects where ...
                      select new
                      {
                          A = o.a,
                          B = o.b
                      }
                  ).Take(10);
Run Code Online (Sandbox Code Playgroud)

  • 您不应该更改Take / Skip方法的顺序吗?Take之后跳过(0)没有意义。感谢您以查询方式提供示例。 (2认同)
  • 不,他是对的。取10,取0取前10个元素。跳过0是没有意义的,永远不应该这样做。而“ Take”和“ Skip”的顺序很重要-“ Skip” 10,“ Take” 10取元素10-20;`Take` 10,`Skip` 10不返回任何元素。 (2认同)

Bit*_*ler 5

不知道这是否对任何人都有帮助,但是我发现它对于我的目的很有用:

private static IEnumerable<T> PagedIterator<T>(IEnumerable<T> objectList, int PageSize)
{
    var page = 0;
    var recordCount = objectList.Count();
    var pageCount = (int)((recordCount + PageSize)/PageSize);

    if (recordCount < 1)
    {
        yield break;
    }

    while (page < pageCount)
    {
        var pageData = objectList.Skip(PageSize*page).Take(PageSize).ToList();

        foreach (var rd in pageData)
        {
            yield return rd;
        }
        page++;
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您将需要一些linq查询,并将结果和页面大小一起传递到foreach循环中:

var results = from a in dbContext.Authors
              where a.PublishDate > someDate
              orderby a.Publisher
              select a;

foreach(var author in PagedIterator(results, 100))
{
    // Do Stuff
}
Run Code Online (Sandbox Code Playgroud)

因此,这将遍历每个作者一次获取100位作者的过程。