Frage
Wie blättern Sie in LINQ durch eine Sammlung, vorausgesetzt, Sie haben eine startIndex
und ein count
?
Lösung
Vor ein paar Monaten habe ich einen Blogbeitrag über Fluent Interfaces und LINQ geschrieben, in dem eine Erweiterungsmethode verwendet wurde IQueryable<T>
und eine weitere Klasse, um die folgende natürliche Art der 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-Codegalerie-Seite erhalten: Pipelines, Filter, Fluent API und LINQ to SQL.
Andere Tipps
Es ist ganz einfach mit dem 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 erstellen musste.Deshalb habe ich zunächst eine Sammlung von Seitenzahlen für die Sammlung der Artikel 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]
Damit könnte ich die Artikelsammlung 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 nutzen Skip
Und Take
zusammen mit der Auswahl des Index aus dem pageRange
oben erstellt:
IEnumerable<IEnumerable<Item>> pageRange
.Select((page, index) =>
myItems
.Skip(index*PageSize)
.Take(PageSize));
Natürlich muss man jede Seite als zusätzliche Sammlung behandeln, aber z.B.Wenn Sie Repeater verschachteln, ist dies eigentlich einfach zu handhaben.
Der einzeiliger TLDR Version wäre diese:
var pages = Enumerable
.Range(0, pageCount)
.Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
Was wie folgt verwendet werden kann:
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 posten, der den gesamten Vorgang (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 es Ihnen jedoch um Leistung geht, und im Produktionscode geht es uns allen um Leistung, sollten Sie nicht das oben gezeigte Paging von LINQ verwenden, sondern das zugrunde liegende IEnumerator
Paging selbst implementieren.Tatsächlich ist es genauso 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 der Verwendung Skip()
für mehrere Male auf „kaskadierende Weise“ bedeutet, dass der „Zeiger“ der Iteration, an der er zuletzt übersprungen wurde, nicht wirklich gespeichert wird.- Stattdessen wird die ursprüngliche Sequenz mit Skip-Aufrufen vorab geladen, was dazu führt, dass die bereits „verbrauchten“ Seiten immer wieder „verbraucht“ werden.- Das können Sie selbst beweisen, wenn Sie die Sequenz erstellen ideas
sodass es zu Nebenwirkungen kommt.-> Auch 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 mit IEnumerable
direkt an die Schnittstelle von 's, merkt sich stattdessen die Position des Endes der letzten logischen Seite, sodass kein explizites Überspringen erforderlich ist und Nebenwirkungen nicht wiederholt werden.