【问题标题】:Paging a collection with LINQ使用 LINQ 对集合进行分页
【发布时间】:2010-09-05 05:32:24
【问题描述】:

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

【问题讨论】:

    标签: linq .net-3.5


    【解决方案1】:

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

    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的接口的变种,会记住最后一个逻辑页面的结束位置,所以不需要显式跳过,也不会重复副作用。

    【讨论】:

      【解决方案2】:

      几个月前,我写了一篇关于 Fluent Interfaces 和 LINQ 的博文,其中使用了 IQueryable&lt;T&gt; 上的扩展方法和另一个类来提供以下对 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

      【讨论】:

        【解决方案3】:

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

        var query = from i in ideas
                    select i;
        
        var paggedCollection = query.Skip(startIndex).Take(count);
        

        【讨论】:

        • 我相信做这样的事情是可以的。他可能有答案,但也许他也想看看其他人能想出什么。
        • 这最初是在 StackOverflow 测试期的第一天发布的,因此文章 ID 为 66。我正在为 Jeff 测试系统。此外,它似乎是有用的信息,而不是有时来自 beta 测试的常见测试废话。
        【解决方案4】:

        我解决这个问题的方式与其他人的解决方案有点不同,因为我必须使用中继器制作自己的分页器。所以我首先为我拥有的项目集合制作了一个页码集合:

        // 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&lt;Item&gt;)。这是您可以使用SkipTake 以及从上面创建的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
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-23
          • 2015-12-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-03
          • 1970-01-01
          相关资源
          最近更新 更多