Domanda

Come si sfoglia una raccolta in LINQ dato che si dispone di un file startIndex e un count?

È stato utile?

Soluzione

Qualche mese fa ho scritto un post sul blog su Fluent Interfaces e LINQ che utilizzava un metodo di estensione IQueryable<T> e un'altra classe per fornire il seguente modo naturale di impaginare una raccolta LINQ.

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

Puoi ottenere il codice dalla pagina Galleria codici MSDN: Pipeline, filtri, API Fluent e LINQ to SQL.

Altri suggerimenti

È molto semplice con Skip E Take metodi di estensione.

var query = from i in ideas
            select i;

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

Ho risolto questo problema in modo leggermente diverso rispetto a quello che hanno gli altri poiché ho dovuto creare il mio impaginatore, con un ripetitore.Quindi per prima cosa ho creato una raccolta di numeri di pagina per la raccolta di elementi che ho:

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

Usando questo potrei facilmente suddividere la raccolta di elementi in una raccolta di "pagine".Una pagina in questo caso è solo una raccolta di elementi (IEnumerable<Item>).Ecco come puoi farlo usando Skip E Take insieme alla selezione dell'indice dal file pageRange creato sopra:

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

Ovviamente devi gestire ogni pagina come una raccolta aggiuntiva ma ad es.se stai nidificando ripetitori, questo è effettivamente facile da gestire.


IL TLDR a una riga la versione sarebbe questa:

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

Che può essere utilizzato in questo modo:

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

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

Questa domanda è un po' vecchia, ma volevo pubblicare il mio algoritmo di paging che mostra l'intera procedura (inclusa l'interazione dell'utente).

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

Tuttavia, se cerchi le prestazioni e, nel codice di produzione, tutti cerchiamo le prestazioni, non dovresti utilizzare il paging di LINQ come mostrato sopra, ma piuttosto il sottostante IEnumerator per implementare tu stesso il cercapersone.In effetti, è semplice come l'algoritmo LINQ mostrato sopra, ma più performante:

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

Spiegazione:Lo svantaggio dell'utilizzo Skip() per più volte in "modo a cascata" è che non memorizzerà realmente il "puntatore" dell'iterazione, dove è stata saltata l'ultima volta.- Invece la sequenza originale verrà caricata frontalmente con chiamate di salto, che porteranno a "consumare" le pagine già "consumate" più e più volte.- Puoi dimostrarlo tu stesso, quando crei la sequenza ideas in modo che produca effetti collaterali.-> Anche se hai saltato 10-20 e 20-30 e desideri elaborare 40+, vedrai tutti gli effetti collaterali di 10-30 eseguiti nuovamente, prima di iniziare a ripetere 40+.La variante utilizzando IEnumerabledirettamente, ricorderà invece la posizione della fine dell'ultima pagina logica, quindi non è necessario saltare esplicitamente e gli effetti collaterali non si ripeteranno.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top