2008-08-01 90 views

回答

34

幾個月前,我寫了一篇博客文章流利的接口和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

59

這是非常簡單的SkipTake擴展方法。

var query = from i in ideas 
      select i; 

var paggedCollection = query.Skip(startIndex).Take(count); 
+3

我認爲,這是確定做這樣的事情。他可能有一個答案,但也許他想看看其他人也可以提出什麼。 – 2008-11-29 03:39:50

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的接口的變體會記住最後一個邏輯頁面末尾的位置,因此不需要顯式跳過,副作用也不會重複。

10

我解決了這個問題,與其他人有點不同,因爲我必須用一箇中繼器製作我自己的分頁器。所以,我首次提出頁碼的集合,我有項目的集合:

// 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>)。這是如何使用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 
    } 
}