代码之家  ›  专栏  ›  技术社区  ›  Nick Berardi

使用Linq分页集合

  •  74
  • Nick Berardi  · 技术社区  · 16 年前

    如果你有一个 startIndex 和A count ?

    4 回复  |  直到 6 年前
        1
  •  38
  •   Adi edson    12 年前

    几个月前,我写了一篇关于fluent接口和linq的博客文章,其中使用了一个扩展方法 IQueryable<T> 以及另一个类,提供以下自然方式对Linq集合进行分页。

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

    您可以从“msdn代码库”页获取代码: Pipelines, Filters, Fluent API and LINQ to SQL .

        2
  •  62
  •   Mathew Thompson    12 年前

    这是非常简单的 Skip Take 扩展方法。

    var query = from i in ideas
                select i;
    
    var paggedCollection = query.Skip(startIndex).Take(count);
    
        3
  •  12
  •   Spoike Otávio Décio    12 年前

    我解决这个有点不同于其他人,因为我必须使自己的异教者,与中继器。因此,我首先收集了以下项目的页码:

    // assumes that the item collection is "myItems"
    
    int pageCount = (myItems.Count + PageSize - 1) / PageSize;
    
    IEnumerable<int> pageRange = Enumerable.Range(1, pageCount);
       // pageRange contains [1, 2, ... , pageCount]
    

    使用这个,我可以很容易地将项目集合划分为一个“页面”集合。本例中的页面只是项目的集合( IEnumerable<Item> )这就是你可以用的方法 Skip Take 以及从中选择索引 pageRange 上面创建的:

    IEnumerable<IEnumerable<Item>> 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<Item> page : pages) 
    {
        // handle page
    
        for (Item item : page) 
        {
            // handle item in page
        }
    }
    
        4
  •  9
  •   Nico    6 年前

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

    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);
    }
    

    说明:使用的缺点 Skip() 多次以“级联方式”表示,它不会真正存储迭代的“指针”,最后一次跳过的地方。-相反,原始序列将被预先加载跳过调用,这将导致“消费”已经“消费”的页面一次又一次。-当你创建序列时,你可以证明你自己 ideas 从而产生副作用。->即使您跳过了10-20和20-30,并且希望处理40+,在开始迭代40+之前,您将看到10-30的所有副作用再次被执行。 变量使用 IEnumerable 的接口将直接记住最后一个逻辑页末尾的位置,因此不需要显式跳过,副作用也不会重复。