Wie blättern Sie in einer Sammlung in LINQ, wenn Sie ein startIndex
und ein haben count
?
Wie blättern Sie in einer Sammlung in LINQ, wenn Sie ein startIndex
und ein haben count
?
Antworten:
Vor einigen Monaten schrieb ich einen Blog-Beitrag über Fluent Interfaces und LINQ, in dem eine Erweiterungsmethode für IQueryable<T>
und eine andere Klasse verwendet wurden, um die folgende natürliche Methode zur Paginierung einer LINQ-Sammlung bereitzustellen.
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
Sie können den Code von der MSDN-Codegalerieseite abrufen: Pipelines, Filter, Fluent API und LINQ to SQL .
Es ist sehr einfach mit den Skip
und Take
Erweiterungsmethoden.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
Ich habe das etwas anders gelöst als die anderen, da ich meinen eigenen Paginator mit einem Repeater herstellen musste. Also habe ich zuerst eine Sammlung von Seitenzahlen für die Sammlung von Gegenständen erstellt, die ich habe:
// 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]
Auf diese Weise könnte ich die Objektsammlung leicht in eine Sammlung von "Seiten" unterteilen. Eine Seite ist in diesem Fall nur eine Sammlung von Elementen ( IEnumerable<Item>
). So können Sie es mit Skip
und Take
zusammen mit der Auswahl des Index aus dem pageRange
oben erstellten tun :
IEnumerable<IEnumerable<Item>> pageRange
.Select((page, index) =>
myItems
.Skip(index*PageSize)
.Take(PageSize));
Natürlich müssen Sie jede Seite als zusätzliche Sammlung behandeln, aber wenn Sie beispielsweise Repeater verschachteln, ist dies tatsächlich einfach zu handhaben.
Die einzeilige TLDR- Version wäre folgende:
var pages = Enumerable
.Range(0, pageCount)
.Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
Welches kann wie folgt verwendet werden:
for (Enumerable<Item> page : pages)
{
// handle page
for (Item item : page)
{
// handle item in page
}
}
Diese Frage ist etwas alt, aber ich wollte meinen Paging-Algorithmus veröffentlichen, der die gesamte Prozedur (einschließlich Benutzerinteraktion) zeigt.
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);
Wenn Sie jedoch nach Leistung streben und im Produktionscode alle nach Leistung streben, sollten Sie das Laging von LINQ nicht wie oben gezeigt verwenden, sondern den zugrunde liegenden IEnumerator
Wert, um das Paging selbst zu implementieren. Tatsächlich ist es so einfach wie der oben gezeigte LINQ-Algorithmus, aber leistungsfähiger:
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);
}
Erläuterung: Der Nachteil einer Skip()
mehrfachen "kaskadierenden Verwendung" besteht darin, dass der "Zeiger" der Iteration, in der er zuletzt übersprungen wurde, nicht wirklich gespeichert wird. - Stattdessen wird die ursprüngliche Sequenz mit Sprungaufrufen von vorne geladen, was dazu führt, dass die bereits "verbrauchten" Seiten immer wieder "verbraucht" werden. - Sie können dies selbst beweisen, wenn Sie die Sequenz ideas
so erstellen , dass sie Nebenwirkungen hervorruft. -> Selbst wenn Sie 10-20 und 20-30 übersprungen haben und 40+ verarbeiten möchten, werden alle Nebenwirkungen von 10-30 erneut ausgeführt, bevor Sie mit der Iteration von 40+ beginnen. Die Variante, die IEnumerable
die Benutzeroberfläche direkt verwendet, merkt sich stattdessen die Position am Ende der letzten logischen Seite, sodass kein explizites Überspringen erforderlich ist und Nebenwirkungen nicht wiederholt werden.