用LINQ分页收集
如果你有一个startIndex
和一个count
,你如何通过LINQ中的一个集合进行分页?
几个月前,我写了一篇关于Fluent Interfaces和LINQ的博客文章,其中使用了IQueryable<T>
的扩展方法,另一个类提供了以下自然分页LINQ集合的方法。
var query = from i in ideas select i; var pagedCollection = query.InPagesOf(10); var pageOfIdeas = pagedCollection.Page(2);
您可以从MSDN代码库页面获取代码: pipe道,filter,Fluent API和LINQ to SQL 。
Skip
和Take
扩展方法非常简单。
var query = from i in ideas select i; var paggedCollection = query.Skip(startIndex).Take(count);
我解决这个问题的方式与其他方式有所不同,因为我必须用自己的中继器来创build自己的分页器。 所以我首先为我收集的物品收集了一些页码:
// 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
和select从pageRange
创build的pageRange
select索引:
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 } }
这个问题有点旧,但我想发布我的分页algorithm,显示整个过程(包括用户交互)。
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直接实现分页。 事实上,它与上面所示的LINQalgorithm一样简单,但性能更高:
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()) { int smallNumber = page.Current; Console.WriteLine(smallNumber); } 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()的缺点是,它不会真正存储迭代的“指针”,它最后被跳过。 相反,原始序列会被跳过调用,这将导致整个序列一遍又一遍地被消耗。 – 当你创build序列ideas
时,你可以certificate自己,这样会产生副作用。 – >即使你已经跳过了10-20和20-30,并想要处理40+,你会看到10-30的所有副作用再次执行,然后再开始迭代40+。 直接使用IEnumerable的变体会记住最后一个逻辑页面结束的位置,所以不需要明确的跳过,副作用也不会重复。