2015-04-08 19 views
10

I starali się wdrożyć stronicowania na dłuższą chwilę teraz i znalazłem ten poradnik dla stronicowania z MVC: ASP.NET MVC Paging Done PerfectlyPaging z PagedList, czy to jest wydajne?

Teraz, w tym roztworze, kwerendy bazy danych dla całego zestawu klientów a potem zwróć listę stronicowanych klientów zamiast zwykłej listy.

Uważam, że jest to niepokojące, ponieważ planuję pokazać tylko 10 lub 20 wpisów na stronie, a moja baza danych z łatwością będzie mieć ponad milion z nich. Dlatego zapytanie do całej bazy danych za każdym razem, gdy chcę wyświetlić stronę Index, wydaje się w najlepszym wypadku kiepskim rozwiązaniem.

Jeśli rozumiem, że coś jest nie tak, proszę, uwolnij mnie teraz, ale dla mnie to rozwiązanie nie jest idealne.

Czy coś źle zrozumiałem? Czy istnieje wydajniejsze rozwiązanie lub biblioteka do dzielenia na strony z MVC?

+0

W jaki sposób przesyłasz zapytanie do bazy danych? – JonE

+2

Samouczek przeznaczony jest dla wersji demonstracyjnej i wykonuje paginację od strony klienta, w której pobiera wszystkie dane za jednym razem, a następnie wykonuje stronicowanie tylko z tego zestawu danych. W twoim przypadku, gdy masz dużo danych, musisz iść z paginacją po stronie serwera. –

+0

@ Glitch100: Pytam DB dokładnie jak w przykładzie: 'List allClients = DB.Client.ToList(); '. –

Odpowiedz

4

Naturalnie stronicowanie będzie wymagać znajomości całkowitej liczby wyników, aby logika mogła określić, ile stron istnieje itp. Jednak zamiast obniżać wszystkie wyniki, po prostu zbuduj zapytanie do bazy danych, aby zwrócić kwotę stronicowaną (np. 30), a także liczba wszystkich wyników.

Na przykład, jeśli były przy użyciu Entity Framework lub LINQ2SQL można zrobić coś takiego

IQueryable<Result> allResults = MyRepository.RetrieveAll(); 

var resultGroup = allResults.OrderByDescending(r => r.DatePosted) 
               .Skip(60) 
               .Take(30) 
               .GroupBy(p => new {Total = allResults.Count()}) 
               .First(); 

var results = new ResultObject 
{ 
    ResultCount = resultGroup.Key.Total, 
    Results = resultGrouping.Select(r => r) 
}; 

Ponieważ nie zrobili .ToList() na naszym wyniku ustawiony dopóki nie sfinalizowane, co chcemy, nie doprowadziliśmy wyników do pamięci. Odbywa się to, gdy wywołujemy .First() na naszym zestawie wyników.

W końcu nasz obiekt, który otrzymamy (ResultObject), może być następnie użyty do późniejszego wywoływania stronicowania. Jak już wiemy, już wiemy, na której stronie jesteśmy (3, gdy pomijaliśmy 60, z 30 na stronę) i mamy wyniki do wyświetlenia.

Inne dokumenty i informacje

How To: Page through Query Results

Server Side Paging with Entity Frame

1

Masz trzy sposoby realizacji paginacji w aplikacji:

  • zmusić do powrotu th Repository e DTO z minimalną ilością danych z powrotem do klienta i używaj niektórych wtyczek jquery, dostarczając stronicowanie samodzielnie. Jest to prosty sposób, ale czasami (jak w twoim przypadku) nie jest to możliwe. Tak więc musisz zaimplementować paginację po stronie serwera
  • Całą kolekcję i zwróć potrzebną stronę z rozszerzeniem LINQ. Myślę, że wiele repozytoriów i ORM robi to wewnętrznie (nie działało z Entity Framework, nie mogę powiedzieć na pewno). Problem z tym rozwiązaniem polega na tym, że musisz zsynchronizować pamięć podręczną i bazę danych, i musisz zdobyć serwer z wystarczającą pamięcią do przechowywania wszystkich danych (lub chmury lub czegoś w tym rodzaju). Podobnie jak w przypadku innych odpowiedzi, można pominąć niepotrzebne dane z leniwymi pracami IEnumerable, więc nie trzeba buforować kolekcji.
  • Wdrażanie stron po stronie DB.Jeśli używasz SQL, możesz użyć konstrukcji ROW_NUMBER, działa albo w MS SQL lub w Oracle lub w MySQL (nie ROW_NUMBER sam w rzeczywistości, tylko analogowy). Jeśli masz rozwiązanie NoSQL, musisz sprawdzić dokumentację.
1

Jeśli pójdziesz do strony github z PagedList addon widać, że jeśli masz metodę zwrócenie IQueryable<T> wtedy magia PagedList może pracować na tym bez powrotu każdy element z bazy danych. Jeśli nie możesz kontrolować, co zwraca zapytanie do bazy danych, musisz polegać na innych metodach.

Przykład z tej strony jest to

public class ProductController : Controller 
{ 
    public object Index(int? page) 
    { 
     var products = MyProductDataSource.FindAllProducts(); //returns IQueryable<Product> representing an unknown number of products. a thousand maybe? 

     var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1) 
     var onePageOfProducts = products.ToPagedList(pageNumber, 25); // will only contain 25 products max because of the pageSize 

     ViewBag.OnePageOfProducts = onePageOfProducts; 
     return View(); 
    } 
} 
2

poradnik do której prowadzi link wygląda dziwnie, ponieważ używa List<Client>. To rzeczywiście zaprowadzi wszystkich klientów do pamięci, a następnie przejdzie przez to. Zamiast tego należy szukać metod, które wykorzystują IQueryable<T>, a konkretnie Skip i Take, więc stronicowania powinien wyglądać

IQueryable<Client> clients = repo.GetClients();   // lazy load - does nothing 
List<Client> paged = clients.Skip(20).Take(10).ToList(); // execute final SQL 

W zależności od tego, co odwzorowujących użyć znajdziesz podobnych metod w EF, NHibernate, LINQ-SQL etc

4

przykładem na github pokazuje, że jest on przy użyciu IQueryable, która jest następnie wykorzystywana przez ToPagedList(), co oznacza, że ​​kod jest bardzo zoptymalizowany i nie będzie sam w sobie zwrócić wszystkie rekordy ...

Patrząc na kod klasy PagedList

// superset is the IQueryable. 
TotalItemCount = superset == null ? 0 : superset.Count(); 

// add items to internal list 
if (superset != null && TotalItemCount > 0) 
    Subset.AddRange(pageNumber == 1 
    ? superset.Skip(0).Take(pageSize).ToList() 
    : superset.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList() 

Tak, jak widać, korzysta już z zalecanych metod stronicowania po stronie serwera pomijania i pobierania, a następnie wykonuje formularz ToList().

Jednak jeśli nie działa z IQueryable, tj IEnumerable następnie używany jest następujący kod:

/// <summary> 
/// Initializes a new instance of the <see cref="PagedList{T}"/> class that divides the supplied superset into subsets the size of the supplied pageSize. The instance then only containes the objects contained in the subset specified by index. 
/// </summary> 
/// <param name="superset">The collection of objects to be divided into subsets. If the collection implements <see cref="IQueryable{T}"/>, it will be treated as such.</param> 
/// <param name="pageNumber">The one-based index of the subset of objects to be contained by this instance.</param> 
/// <param name="pageSize">The maximum size of any individual subset.</param> 
/// <exception cref="ArgumentOutOfRangeException">The specified index cannot be less than zero.</exception> 
/// <exception cref="ArgumentOutOfRangeException">The specified page size cannot be less than one.</exception> 
public PagedList(IEnumerable<T> superset, int pageNumber, int pageSize) 
     : this(superset.AsQueryable<T>(), pageNumber, pageSize) 
    { 
    } 

Kwestia jest taka, że ​​w zależności od filtrowania wykorzystywanych aby uzyskać IEnumerable w pierwszej kolejności mogłyby zawierać wszystkie rekordy, więc użyj IQueryable, gdzie to możliwe, aby uzyskać optymalną wydajność PagedList.

0

Ten komponent (PagedList) działa dobrze dla dużej liczby rekordów, za pierwszym razem i za każdym razem, gdy wybierzesz stronę, wykona 2 wywołania do bazy danych. Jeden zwróci liczbę rekordów, a drugi zwróci tylko rekordy wybranej strony. Tylko pamiętaj, aby nie wywołać metodę ToList()

-1
  1. kod z obiektu WebGrid w .cshtml & będzie całkiem ok.
  2. Złożoność stronicowania będzie dość niska.
  3. czysty kod.
  4. Klasa Micro Soft BCL. mniej błędów.