Nasıl bir olduğu kabul edilirse LINQ bir koleksiyon aracılığıyla size sayfasını yapmak startIndex
ve count
?
Nasıl bir olduğu kabul edilirse LINQ bir koleksiyon aracılığıyla size sayfasını yapmak startIndex
ve count
?
Yanıtlar:
Birkaç ay önce IQueryable<T>
, bir LINQ koleksiyonunu sayfalandırmanın aşağıdaki doğal yolunu sağlamak için bir Uzantı Yöntemi ve başka bir sınıf kullanan Fluent Interfaces ve LINQ hakkında bir blog yazısı yazdım .
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
Kodu MSDN Kod Galerisi Sayfasından alabilirsiniz: Pipelines, Filters, Fluent API ve LINQ to SQL .
Skip
Ve Take
uzatma yöntemleri ile çok basittir .
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
Tekrarlayıcı ile kendi sayfalandırıcımı yapmak zorunda kaldığım için bunu diğerlerinin sahip olduğundan biraz farklı çözdüm. Bu nedenle, önce sahip olduğum öğelerin koleksiyonu için bir sayfa numarası koleksiyonu yaptım:
// 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]
Bunu kullanarak, öğe koleksiyonunu "sayfalar" koleksiyonuna kolayca bölebilirim. Bu durumda bir sayfa yalnızca öğelerden ( IEnumerable<Item>
) oluşan bir koleksiyondur . Yukarıda oluşturulan dizini seçerek Skip
ve Take
birlikte bunu nasıl yapabilirsiniz pageRange
:
IEnumerable<IEnumerable<Item>> pageRange
.Select((page, index) =>
myItems
.Skip(index*PageSize)
.Take(PageSize));
Elbette her sayfayı ek bir koleksiyon olarak ele almalısınız, ancak örneğin, tekrarlayıcıları iç içe yerleştiriyorsanız, bu aslında kolay bir işlemdir.
Tek satırlık TLDR versiyonu şu şekilde olacaktır:
var pages = Enumerable
.Range(0, pageCount)
.Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
Hangisi şu şekilde kullanılabilir:
for (Enumerable<Item> page : pages)
{
// handle page
for (Item item : page)
{
// handle item in page
}
}
Bu soru biraz eski, ancak tüm prosedürü (kullanıcı etkileşimi dahil) gösteren çağrı algoritmamı göndermek istedim.
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);
Bununla birlikte, eğer performansın peşindeyseniz ve üretim kodundaysa, hepimiz performansın peşindeyiz, LINQ'nun sayfalandırmasını yukarıda gösterildiği gibi kullanmamalısınız, bunun yerine IEnumerator
sayfalamayı kendiniz uygulamak için temelini kullanmalısınız. Aslında, yukarıda gösterilen LINQ algoritması kadar basit, ancak daha performanslı:
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);
}
Açıklama: Skip()
"Basamaklı" olarak birden çok kez kullanmanın dezavantajı , yinelemenin "işaretçisini" en son atlandığı yerde gerçekten depolamayacak olmasıdır. - Bunun yerine, orijinal sıra atlama çağrılarıyla önden yüklenecek ve bu da zaten "tüketilmiş" sayfaların tekrar tekrar "tüketilmesine" yol açacaktır. - Sıralamayı oluşturduğunuzda, ideas
yan etkilere yol açacak şekilde bunu kendiniz kanıtlayabilirsiniz . -> 10-20 ve 20-30'u atlamış olsanız ve 40+ işlem yapmak istiyorsanız bile, 40+ yinelemeye başlamadan önce 10-30'un tüm yan etkilerinin yeniden yürütüldüğünü göreceksiniz. IEnumerable
Arayüzünü doğrudan kullanan varyant , bunun yerine son mantıksal sayfanın sonunun konumunu hatırlayacaktır, bu nedenle açık atlama gerekmez ve yan etkiler tekrarlanmaz.