2008-08-01 14 views

Odpowiedz

34

Kilka miesięcy temu napisałem blogu o Fluent interfejsów i LINQ, który wykorzystał metodę rozszerzenia na IQueryable<T> i innej klasy, aby podać następujące naturalny sposób Stronicowanie zbiór LINQ.

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

Możesz pobrać kod z Galerii kodów MSDN Strona: Pipelines, Filters, Fluent API and LINQ to SQL.

59

Jest to bardzo proste z metodami rozszerzenia Skip i Take.

var query = from i in ideas 
      select i; 

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

wierzę, że to OK, aby zrobić coś takiego. Mógł mieć odpowiedź, ale może chce zobaczyć, co też mogą wymyślić inni ludzie. –

+11

Ten został pierwotnie opublikowany w pierwszym dniu okresu beta StackOverflow, a więc 66 dla identyfikatora artykułu. Testowałem system, dla Jeffa. Poza tym wydawało się przydatne informacje zamiast zwykłych bzdur testowych, które czasami pochodzą z testów beta. –

6

To pytanie jest nieco stare, ale chciałem opublikować mój algorytm stronicowania, który pokazuje całą procedurę (w tym interakcje użytkownika).

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

Jednakże, jeśli jesteś po wydajności, a w kodzie produkcyjnym, wszyscy jesteśmy po wykonaniu, nie należy używać stronicowania LINQ, jak przedstawiono powyżej, lecz instrumentem bazowym IEnumerator wdrożyć stronicowania siebie. W rzeczywistości jest to tak proste jak w Linq algorytmu przedstawionego powyżej, ale bardziej wydajnych:

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

Objaśnienie: Wadą stosowania Skip() do wielu razy w „kaskadowego” sposób jest to, że nie będzie tak naprawdę nie przechowuje "wskaźnika" iteracji, gdzie był ostatnio pomijany. - Zamiast tego oryginalna sekwencja zostanie załadowana z wyprzedzeniem z pominięciem wywołań, co doprowadzi do "konsumowania" już "zużytych" stron w kółko. - Możesz to udowodnić samodzielnie, gdy utworzysz sekwencję ideas, dzięki czemu uzyska ona efekty uboczne. -> Nawet jeśli pominąłeś 10-20 i 20-30 i chcesz przetworzyć 40+, zobaczysz wszystkie efekty uboczne 10-30, które zostaną ponownie wykonane, zanim zaczniesz powtarzać 40+. Wariant wykorzystujący bezpośrednio interfejs IEnumerable zapamięta położenie końca ostatniej strony logicznej, więc nie jest konieczne żadne pominięcie jawne, a efekty uboczne nie będą powtarzane.

10

Rozwiązałem to nieco inaczej niż inni, ponieważ musiałem stworzyć własny paginator z repeaterami. Więc raz pierwszy zbiór numerów stron do gromadzenia przedmiotów, które mam:

// 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] 

Używanie tego mogę łatwo podzielić kolekcję element do kolekcji „stron”. Strona w tym przypadku to tylko zbiór przedmiotów (IEnumerable<Item>). W ten sposób można zrobić to za pomocą Skip i Take wraz z wybierając indeks z pageRange utworzonego powyżej:

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

oczywiście trzeba obsłużyć każdą stronę jako dodatkowy kolekcji ale np jeśli budujesz wzmacniacze, to jest to łatwe w obsłudze.


jedno-liner TLDR wersja byłaby to:

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

które mogą być wykorzystane jako to:

for (Enumerable<Item> page : pages) 
{ 
    // handle page 

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