Як ви переглядаєте колекцію в LINQ, враховуючи, що у вас є a startIndex
та a count
?
Як ви переглядаєте колекцію в LINQ, враховуючи, що у вас є a startIndex
та a count
?
Відповіді:
Кілька місяців тому я написав допис у блозі про вільні інтерфейси та LINQ, який використовував метод розширення IQueryable<T>
та інший клас, щоб запропонувати наступний природний спосіб розміщення сторінок на колекціях LINQ.
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
Ви можете отримати код зі сторінки галереї кодів MSDN: трубопроводи, фільтри, API Fluent та LINQ to SQL .
Це дуже просто з методами Skip
і Take
розширення.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
Я вирішив це дещо інакше, ніж те, що мають інші, оскільки мені довелося зробити власний пагінатор, з ретранслятором. Тож я спочатку склав колекцію номерів сторінок для колекції предметів, які у мене є:
// 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
}
}
Це питання дещо застаріле, але я хотів опублікувати свій алгоритм підкачки, який показує всю процедуру (включаючи взаємодію користувача).
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
безпосередньо інтерфейс, замість цього запам'ятає позицію кінця останньої логічної сторінки, тому не потрібно явного пропуску, а побічні ефекти не будуть повторюватися.