2008-08-01 42 views

Trả lời

34

Một vài tháng trở lại tôi đã viết một bài đăng blog về thạo giao diện và LINQ mà đã sử dụng một phương pháp mở rộng trên IQueryable<T> và lớp khác để cung cấp các cách tự nhiên sau đây của một bộ sưu tập paginating LINQ.

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

Bạn có thể lấy mã từ Thư viện mã MSDN Trang: Pipelines, Filters, Fluent API and LINQ to SQL.

59

Rất đơn giản với các phương pháp mở rộng SkipTake.

var query = from i in ideas 
      select i; 

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

Tôi tin rằng đó là OK để làm một cái gì đó như thế này. Anh ta có thể có một câu trả lời nhưng có lẽ anh ta muốn xem những gì người khác có thể nghĩ ra. –

+11

Điều này ban đầu được đăng trong ngày đầu tiên của giai đoạn beta của StackOverflow, do đó số 66 cho ID bài viết. Tôi đã thử nghiệm hệ thống, cho Jeff. Thêm vào đó nó có vẻ giống như thông tin hữu ích thay vì crap thử nghiệm thông thường mà đôi khi đi ra khỏi thử nghiệm beta. –

6

Câu hỏi này hơi cũ, nhưng tôi muốn đăng thuật toán phân trang hiển thị toàn bộ quy trình (bao gồm tương tác của người dùng).

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

Tuy nhiên, nếu bạn là sau màn trình diễn, và trong mã sản xuất, chúng tôi tất cả sau khi thực hiện, bạn không nên sử dụng phân trang LINQ như trình bày ở trên, mà là tiềm ẩn IEnumerator để thực hiện phân trang cho mình. Như một vấn đề của thực tế, nó là đơn giản như LINQ-thuật toán trình bày ở trên, nhưng performant hơn:

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

Giải thích: Nhược điểm của việc sử dụng Skip() cho nhiều lần trong một "cách tầng" là, rằng nó sẽ không thực sự lưu trữ "con trỏ" của lần lặp, nơi nó được bỏ qua lần cuối. - Thay vào đó, chuỗi gốc sẽ được tải trước bằng các cuộc gọi bỏ qua, điều này sẽ dẫn đến việc "tiêu thụ" các trang "đã tiêu thụ" đã hết lần này đến lần khác. - Bạn có thể chứng minh rằng bản thân bạn, khi bạn tạo chuỗi ideas để nó mang lại hiệu ứng phụ. -> Ngay cả khi bạn đã bỏ qua 10-20 và 20-30 và muốn xử lý 40+, bạn sẽ thấy tất cả các tác dụng phụ của 10-30 được thực thi lại, trước khi bạn bắt đầu lặp lại 40+. Biến thể sử dụng trực tiếp giao diện của IEnumerable, thay vào đó sẽ nhớ vị trí của phần cuối của trang logic cuối cùng, do đó không cần bỏ qua rõ ràng và các hiệu ứng phụ sẽ không được lặp lại.

10

Tôi đã giải quyết vấn đề này một chút khác với những gì mà những người khác có khi tôi phải làm người tạo trang riêng của mình, với bộ lặp. Vì vậy, trước tiên tôi đã tạo một tập hợp số trang cho bộ sưu tập các mục mà tôi có:

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

Sử dụng điều này tôi có thể dễ dàng phân chia bộ sưu tập mục thành tập hợp "trang". Một trang trong trường hợp này chỉ là một tập hợp các mục (IEnumerable<Item>). Đây là cách bạn có thể làm điều đó bằng SkipTake cùng với việc lựa chọn các chỉ số từ pageRange tạo ở trên:

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

Tất nhiên bạn phải xử lý mỗi trang như một bộ sưu tập thêm nhưng ví dụ nếu bạn đang làm tổ lặp lại thì điều này thực sự dễ xử lý.


Các một liner TLDR phiên bản sẽ là:

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

Mà có thể được sử dụng như thế này:

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

    for (Item item : page) 
    { 
     // handle item in page 
    } 
} 
Các vấn đề liên quan