Пейджинг коллекции с помощью LINQ

Как вы просматриваете коллекцию в LINQ, если у вас есть a startIndexи a count?

1.08.2008 13:20:46
4 ОТВЕТА
РЕШЕНИЕ

Несколько месяцев назад я написал сообщение в блоге о Fluent Interfaces и LINQ, в котором использовался метод расширения IQueryable<T>и другой класс, чтобы обеспечить следующий естественный способ разбиения на страницы коллекции LINQ.

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

Вы можете получить код со страницы галереи кодов MSDN: Конвейеры, Фильтры, Свободный API и LINQ to SQL .

41
9.08.2012 14:59:49

Это очень просто с Skipи Takeрасширения методов.

var query = from i in ideas
            select i;

var paggedCollection = query.Skip(startIndex).Take(count);
62
4.05.2012 09:04:26
Я считаю, что это нормально делать что-то подобное. У него может быть ответ, но, возможно, он хочет посмотреть, что могут предложить и другие люди.
Outlaw Programmer 29.11.2008 03:39:50
Первоначально он был опубликован в первый день бета-периода StackOverflow, таким образом, 66 для идентификатора статьи. Я тестировал систему для Джеффа. Кроме того, это показалось полезной информацией вместо обычного теста, который иногда выходит из бета-тестирования.
Nick Berardi 30.11.2008 20:35:33

Я решил это немного по-другому, чем другие, потому что мне пришлось делать свой собственный пагинатор с ретранслятором. Итак, я сначала сделал коллекцию номеров страниц для коллекции предметов, которые у меня есть:

// 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>). Вот как вы можете сделать это используя Skipи Takeвместе с выбором индекса из 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
    }
}
13
20.03.2012 13:55:22

Этот вопрос несколько устарел, но я хотел опубликовать свой алгоритм подкачки, который показывает всю процедуру (включая взаимодействие с пользователем).

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+, вы увидите, что все побочные эффекты 10-30 будут выполнены снова, прежде чем вы начнете повторять 40+. Вариант, использующий IEnumerableнапрямую интерфейс, вместо этого запомнит позицию конца последней логической страницы, поэтому явного пропуска не требуется, и побочные эффекты повторяться не будут.

9
3.03.2018 18:41:55