Пейджинг с PagedList, эффективен ли он?

Я пытаюсь реализовать пейджинг уже довольно давно, и я нашел этот учебник для подкачки с MVC: ASP.NET MVC Paging Done Perfectly

Теперь в этом решении я запрашиваю базу данных для всего набора клиентов, а затем возвращаю вычисленный список клиентов вместо обычного списка.

Я нахожу это тревожным, потому что я планирую показать только 10 или 20 записей на странице, и моя база данных будет легко иметь более миллиона из них. Таким образом, запрос всей базы данных каждый раз, когда я хочу показать страницу Index, в лучшем случае, является плохим решением.

Если я понимаю что-то не так, пожалуйста, не стесняйтесь меня перерезать прямо сейчас, но для меня это решение ничего, кроме совершенного.

Я что-то неправильно понял? Существует ли более эффективное решение или библиотека для разбивки на страницы с помощью MVC?

Ответ 1

Естественно, подкачка потребует знания общего количества результатов, чтобы логика определяла, сколько страниц есть и т.д. Однако вместо того, чтобы свести все результаты, просто создайте свой запрос в базе данных, чтобы вернуть вычисленную сумму (например, 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 на страницу), и у нас есть результаты для отображения.

Дальнейшее чтение и информация

Как: Страница через результаты запроса

Пейджинг на стороне сервера с кадром Entity

Ответ 2

В примере на 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.

Ответ 3

Связанный учебник выглядит странно, потому что он использует 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 и т.д.

Ответ 4

У вас есть три способа реализовать разбиение на страницы в приложении:

  • Подключите Repository, чтобы вернуть DTO с минимальным объемом данных обратно клиенту и использовать некоторые из jquery плагинов, которые сами предоставляют разбиение на страницы. Это простой способ, но иногда (как в вашем случае) это не вариант. Таким образом, вы должны реализовать разбиение на страницы на стороне сервера
  • Вся коллекция кеша и верните необходимую страницу с расширением LINQ. Я думаю, что многие из репозиториев и ORM делают это внутри (не работает с Entity Framework, не могу сказать точно). Проблема с этим решением заключается в том, что вам нужно синхронизировать кеш и db, и вам нужно получить достаточно памяти для хранения всех ваших данных (или облака или что-то еще). Как и в других ответах, вы можете пропустить ненужные данные с ленивой работой IEnumerable, поэтому вам не нужно кэшировать коллекцию.
  • Реализовать разбиение на страницы на стороне БД. Если вы используете SQL, вы можете использовать конструкцию ROW_NUMBER, она работает либо в MS SQL, либо в Oracle или в MySQL (а не ROW_NUMBER сам по себе, только аналоговый). Если у вас есть решение NoSQL, тогда вам нужно проверить документацию.

Ответ 5

Если вы перейдете на страницу github PagedList addon, вы увидите, что если у вас есть метод, возвращающий 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();
    }
}

Ответ 6

Этот компонент (PagedList) отлично работает для большого количества записей, первый раз и каждый раз, когда вы выбираете страницу, он будет делать 2 вызова в базе данных. Один возвращает количество записей, а другой возвращает только записи выбранной страницы. Просто убедитесь, что вы не вызываете метод ToList()

Ответ 7

  • Код с объектом Webgrid в .cshtml, и все будет хорошо.
  • Сложность подкачки будет довольно низкой.
  • чистый код.
  • Класс Micro Soft BCL. меньше ошибок.