Страниране с PagedList, ефективно ли е?

Опитвам се да внедря страниране от доста време и намерих този урок за страниране с MVC: ASP.NET MVC страниране, направено перфектно

Сега, в това решение, отправям запитване към базата данни за целия набор от клиенти и след това връщам списък с клиенти, подреден на страници, вместо нормален списък.

Намирам това за обезпокоително, защото планирам да показвам само 10 или 20 записа на страница, а моята база данни лесно ще има над един милион от тях. По този начин запитването към цялата база данни всеки път, когато искам да покажа страницата Index, изглежда в най-добрия случай лошо решение.

Ако разбирам нещо погрешно, моля, не се колебайте да ме отрежете веднага, но за мен това решение е всичко друго, но не и перфектно.

Да не съм разбрал нещо погрешно? Има ли по-ефективно решение или библиотека за страниране с MVC?


person Flame_Phoenix    schedule 08.04.2015    source източник
comment
Как правите заявки в db?   -  person JonE    schedule 08.04.2015
comment
Урокът е за демонстрация и изпълнява страниране от страна на клиента, при което ще получи всички данни наведнъж и след това ще извърши страниране само от този набор от данни. Във вашия случай, докато имате много данни, трябва да отидете със страниране от страна на сървъра.   -  person Ruchir Shah    schedule 08.04.2015
comment
@Glitch100: Отправям запитване към DB точно както в примера: List<Client> allClients = DB.Client.ToList();.   -  person Flame_Phoenix    schedule 08.04.2015
comment
@RuchirShah: Има ли добри библиотеки за това с помощта на MVC C#? Препоръчвате ли някой?   -  person Flame_Phoenix    schedule 08.04.2015
comment
@Flame_Phoenix: Няма какво много да се направи, трябва да предадете някои параметри на сървъра и да направите заявка в DB според тях. Пример стъпка по стъпка, ще намерите всичко от тук   -  person Ruchir Shah    schedule 08.04.2015
comment
Имам решение, но зависи от това за какво използвате това действие? За да се актуализират данните за регистрирани членове?   -  person JonE    schedule 10.04.2015


Отговори (7)


Естествено странирането ще изисква познаване на общия брой резултати, за да може логиката да определи колко страници има и т.н. Но вместо да свалите всички резултати, просто създайте вашата заявка към базата данни, за да върнете странираната сума (напр. 30) и като както и преброяването на всички резултати.

Например, ако използвате Entity Framework или LINQ2SQL, можете да направите нещо подобно

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

Тъй като не сме направили .ToList() на нашия набор от резултати, докато не финализираме това, което искаме, не сме пренесли резултатите в паметта. Това се прави, когато извикаме .First() на нашия набор от резултати.

Накрая нашият обект, който получаваме (ResultObject), може да се използва за извършване на странирането по-късно. Тъй като имаме броя, вече знаем на коя страница се намираме (3, тъй като пропуснахме 60, с 30 на страница) и имаме резултатите за показване.

Допълнителна литература и информация

Как да: Прелистване на резултатите от заявката

Страниране от страна на сървъра с рамка на обект

person JonE    schedule 08.04.2015

Ако отидете на страницата на github на PagedList добавката, можете да видите, че ако имате метод, който връща IQueryable<T> тогава магията PagedList може да работи върху това, без да връща всеки елемент от базата данни. Ако не можете да контролирате какво ви връща заявката от базата данни, тогава трябва да разчитате на други методи.

Примерът от тази страница е следният

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();
    }
}
person Loofer    schedule 08.04.2015

Примерът на github илюстрира, че той използва IQueryable, който след това се използва от ToPagedList(), което предполага, че кодът е доста оптимизиран и сам по себе си няма да върне всички записи...

Разглеждане на кода на клас 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()

Така че, както можете да видите, той вече използва препоръчаните методи за пейджинг от страна на сървъра за прескачане и вземане и след това предварително формира ToList().

Ако обаче не сте работили с IQueryable, т.е. IEnumerable, тогава се използва следният код:

/// <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)
    {
    }

Проблемът е, че в зависимост от филтрирането, използвано за получаване на IEnumerable на първо място, може да съдържа всички записи, така че използвайте IQueryable, където е възможно за оптимална производителност на PagedList.

person Paul Zahra    schedule 08.04.2015

Свързаният урок изглежда странно, защото използва List<Client>. Това наистина ще пренесе всички клиенти в паметта и след това ще ги прегледа. Вместо това трябва да потърсите методи, които използват IQueryable<T>, по-специално Skip и Take, така че странирането трябва да изглежда така

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

В зависимост от картографите, които използвате, ще намерите подобни методи в EF, NHibernate, Linq-to-SQL и т.н.

person oleksii    schedule 08.04.2015

Имате три начина да внедрите страниране във вашето приложение:

  • Принудете вашия Repository да върне DTO с минимално количество данни обратно на клиента и използвайте някои от jquery добавките, осигуряващи сами странирането. Това е прост начин, но понякога (както във вашия случай) това не е опция. Така че трябва да внедрите страниране от страна на сървъра
  • Кеширайте цялата колекция и върнете необходимата страница с разширение LINQ. Мисля, че много от хранилищата и ORM правят това вътрешно (не работи с Entity Framework, не мога да кажа със сигурност). Проблемът с това решение е, че трябва да синхронизирате кеша и db и трябва да получите сървър с достатъчно памет, за да съхранявате всичките си данни (или да отидете в облак, или нещо подобно). Както в други отговори, можете да пропуснете ненужните данни с мързелива IEnumerable работа, така че не е необходимо да кеширате колекцията..
  • Приложете пагинация от страната на DB. Ако използвате SQL, можете да използвате конструкция ROW_NUMBER, тя работи или в MS SQL или в Oracle или в MySQL (всъщност не самият ROW_NUMBER, само аналогов). Ако имате NoSQL решение, тогава трябва да проверите документацията за него.
person VMAtm    schedule 08.04.2015

Този компонент (PagedList) работи добре за голям брой записи, първия път и всеки път, когато изберете страница, той ще направи 2 извиквания към базата данни. Единият връща броя на записите, а другият ще връща само записите на избраната страница. Просто се уверете, че не извиквате метода ToList().

person foluis    schedule 05.08.2016

  1. Кодирайте с обект Webgrid в .cshtml и ще бъде доста добре.
  2. Сложността на пейджинг ще бъде доста ниска.
  3. чист код.
  4. Micro Soft BCL клас. по-малко грешки.
person ManjunathMayurRK    schedule 05.08.2016