用LINQ分页收集

如果你有一个 startIndex 和一个 count ,你如何在LINQ中对一个集合进行分页?

0
额外 编辑
意见: 13

4 答案

使用 SkipTake 扩展方法非常简单。

var query = from i in ideas
            select i;

var paggedCollection = query.Skip(startIndex).Take(count);
0
额外
这最初是在StackOverflow测试阶段的第一天发布的,因此文章ID为66。我正在测试系统,杰夫。此外,它似乎是有用的信息,而不是通常的测试废话,有时来自beta测试。
额外 作者 Nick Berardi,
我相信可以做这样的事情。他可能有一个答案,但也许他想看看其他人也可以提出什么。
额外 作者 Outlaw Programmer,

A few months back I wrote a blog post about Fluent Interfaces and LINQ which used an Extension Method on IQueryable and another class to provide the following natural way of paginating a LINQ collection.

var query = from i in ideas
            select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);

您可以从MSDN代码库页面获取代码:管道,过滤器,Fluent API和LINQ to SQL

0
额外

这个问题有点旧,但我想发布我的分页算法,它显示了整个过程(包括用户交互)。

const int pageSize = 10;
const int count = 100;
const int startIndex = 20;

int took = 0;
bool getNextPage;
var page = ideas.Skip(startIndex);

do
{
    Console.WriteLine("Page {0}:", (took / pageSize) + 1);
    foreach (var idea in page.Take(pageSize))
    {
        Console.WriteLine(idea);
    }

    took += pageSize;
    if (took < count)
    {
        Console.WriteLine("Next page (y/n)?");
        char answer = Console.ReadLine().FirstOrDefault();
        getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer);

        if (getNextPage)
        {
            page = page.Skip(pageSize);
        }
    }
}
while (getNextPage && took < count);

但是,如果你在性能和生产代码之后,我们都在性能之后,你不应该像上面那样使用LINQ的分页,而应该使用底层的 IEnumerator 来自己实现分页。事实上,它与上面所示的LINQ算法一样简单,但性能更高:

const int pageSize = 10;
const int count = 100;
const int startIndex = 20;

int took = 0;
bool getNextPage = true;
using (var page = ideas.Skip(startIndex).GetEnumerator())
{
    do 
    {
        Console.WriteLine("Page {0}:", (took / pageSize) + 1);

        int currentPageItemNo = 0;
        while (currentPageItemNo++ < pageSize && page.MoveNext())
        {
            var idea = page.Current;
            Console.WriteLine(idea);
        }

        took += pageSize;
        if (took < count)
        {
            Console.WriteLine("Next page (y/n)?");
            char answer = Console.ReadLine().FirstOrDefault();
            getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer);
        }
    }
    while (getNextPage && took < count);
}

Explanation: The downside of using Skip() for multiple times in a "cascading manner" is, that it will not really store the "pointer" of the iteration, where it was last skipped. - Instead the original sequence will be front-loaded with skip calls, which will lead to "consuming" the already "consumed" pages over and over again. - You can prove that yourself, when you create the sequence ideas so that it yields side effects. -> Even if you have skipped 10-20 and 20-30 and want to process 40+, you'll see all side effects of 10-30 being executed again, before you start iterating 40+. The variant using IEnumerable's interface directly, will instead remember the position of the end of the last logical page, so no explicit skipping is needed and side effects won't be repeated.

0
额外

我解决这个问题的方式与其他方式有所不同,因为我必须用一个中继器制作自己的分页器。所以我首先为我收集的物品收集了一些页码:

// assumes that the item collection is "myItems"

int pageCount = (myItems.Count + PageSize - 1) / PageSize;

IEnumerable pageRange = Enumerable.Range(1, pageCount);
   // pageRange contains [1, 2, ... , pageCount]

Using this I could easily partition the item collection into a collection of "pages". A page in this case is just a collection of items (IEnumerable). This is how you can do it using Skip and Take together with selecting the index from the pageRange created above:

IEnumerable> pageRange
    .Select((page, index) => 
        myItems
            .Skip(index*PageSize)
            .Take(PageSize));

当然,您必须处理每个页面作为附加收集,但例如,如果你正在嵌套中继器,那么这实际上很容易处理。


单行TLDR 版本是这样的:

var pages = Enumerable
    .Range(0, pageCount)
    .Select((index) => myItems.Skip(index*PageSize).Take(PageSize));

这可以用作:

for (Enumerable page : pages) 
{
    // handle page

    for (Item item : page) 
    {
        // handle item in page
    }
}
0
额外