Постраничная навигация

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 09.12.2019

Пагинация или постраничный вывод позволяет разбить набор объектов на отдельные страницы. Подобный механизм особенно удобен, если данных очень много - десятки, сотни и даже тысячи объектов. Рассмотрим пару способов, как добавить в проект постраничную навигацию.

Для работы возьмем модели User и Company:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int CompanyId { get; set; }
    public Company Company { get; set; }
}

public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<User> Users { get; set; }
    public Company()
    {
        Users = new List<User>();
    }
}

Для взаимодействия с MS SQL Server через Entity Framework добавим в проект через Nuget пакет Microsoft.EntityFrameworkCore.SqlServer. А затем добавим в проект класс контекста данных UsersContext:

using Microsoft.EntityFrameworkCore;

namespace PagerApp.Models
{
    public class UsersContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        public DbSet<Company> Companies { get; set; }
        public UsersContext(DbContextOptions<UsersContext> options)
            : base(options)
        {
			Database.EnsureCreated();
        }
    }
}

И в классе Startup в методе ConfigureServices() настроим использование этого контекста данных:

public void ConfigureServices(IServiceCollection services)
{
	string connection = "Server=(localdb)\\mssqllocaldb; Database=userstoredb; Trusted_Connection=true;";
	services.AddDbContext<UsersContext>(options => options.UseSqlServer(connection));
	services.AddControllersWithViews();
}

Далее в проект новый класс PageViewModel, который будет содержать всю информацию о пагинации:

using System;

namespace PagerApp.Models
{
    public class PageViewModel
    {
        public int PageNumber { get; private set; }
        public int TotalPages { get; private set; }

        public PageViewModel(int count, int pageNumber, int pageSize)
        {
            PageNumber = pageNumber;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);
        }

        public bool HasPreviousPage
        {
            get
            {
                return (PageNumber > 1);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (PageNumber < TotalPages);
            }
        }
    }
}

Данный класс хранит номер текущей страницы в свойстве PageNumber, общее количество страниц в свойстве TotalPages, а также определяет свойства HasPreviousPage и HasNextPage, с помощью которых можно узнать, есть ли до и после текущей страницы еще какие-нибудь страницы.

Определим также в папке Models класс IndexViewModel, который будет инкапсулировать информацию о пагинации и полученных пользователей:

using System.Collections.Generic;

namespace PagerApp.Models
{
    public class IndexViewModel
    {
        public IEnumerable<User> Users { get; set; }
        public PageViewModel PageViewModel { get; set; }
    }
}

В итоге весь проект будет выглядеть следующим образом:

Постраничный вывод в ASP.NET Core MVC

Используем все эти классы в контроллере HomeController:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PagerApp.Models;
using System.Linq;
using System.Threading.Tasks;

namespace PagerApp.Controllers
{
    public class HomeController : Controller
    {
        UsersContext db;
        public HomeController(UsersContext context)
        {
            this.db = context;
			// добавляем начальные данные
			if (db.Companies.Count() == 0)
            {
                Company oracle = new Company { Name = "Oracle" };
                Company google = new Company { Name = "Google" };
                Company microsoft = new Company { Name = "Microsoft" };
                Company apple = new Company { Name = "Apple" };

                User user1 = new User { Name = "Олег Васильев", Company = oracle, Age = 26 };
                User user2 = new User { Name = "Александр Овсов", Company = oracle, Age = 24 };
                User user3 = new User { Name = "Алексей Петров", Company = microsoft, Age = 25 };
                User user4 = new User { Name = "Иван Иванов", Company = microsoft, Age = 26 };
                User user5 = new User { Name = "Петр Андреев", Company = microsoft, Age = 23 };
                User user6 = new User { Name = "Василий Иванов", Company = google, Age = 23 };
                User user7 = new User { Name = "Олег Кузнецов", Company = google, Age = 25 };
                User user8 = new User { Name = "Андрей Петров", Company = apple, Age = 24 };

                db.Companies.AddRange(oracle, microsoft, google, apple);
                db.Users.AddRange(user1, user2, user3, user4, user5, user6, user7, user8);
                db.SaveChanges();
            }
        }
        public async Task<IActionResult> Index(int page=1)
        {
            int pageSize = 3;	// количество элементов на странице
			
            IQueryable<User> source = db.Users.Include(x=>x.Company);
            var count = await source.CountAsync();
            var items = await source.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();

            PageViewModel pageViewModel = new PageViewModel(count, page, pageSize);
            IndexViewModel viewModel = new IndexViewModel
            {
                PageViewModel = pageViewModel,
                Users =items
            };
            return View(viewModel);
        }
    }
}

Сама пагинация производится в методе Index(). В этот метод передается номер страницы. Если номер страницы явным образом не передан, то считается, что мы получаем данные для первой страницы.

Далее получаем общее количество элементов в переменную count:

var count = await source.CountAsync();

Затем собственно выполняется пагинация - с помощью метода Skip() пропускаем нужное количество элементов, чтобы достичь нужной страницы. И с помощью метода Take() выбираем нужную порцию элементов, которая равна размеру страницы.

И далее возвращается объект IndexViewModel, который хранит всю информацию о пагинации и который передается в представление.

Изменим код представления Index.cshtml:

@using PagerApp.Models
@model IndexViewModel
@{
    ViewData["Title"] = "Home Page";
}
    <style>
        @@font-face {
            font-family: 'FontAwesome';
            src: url('https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2') format('woff2'), 
			url('https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/fonts/fontawesome-webfont.woff') format('woff'), 
			url('https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/fonts/fontawesome-webfont.ttf') format('truetype');
            font-weight: normal;
            font-style: normal
        }

        .glyphicon {
            display: inline-block;
            font: normal normal normal 14px/1 FontAwesome;
            font-size: inherit;
            text-rendering: auto;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale
        }

        .glyphicon-chevron-right:before {
            content: "\f054";
        }

        .glyphicon-chevron-left:before {
            content: "\f053";
        }
    </style>
<h1>Список пользователей</h1>
<table class="table">
    <tr><th>Имя</th><th>Возраст</th><th>Компания</th></tr>
    @foreach (User u in Model.Users)
    {
        <tr><td>@u.Name</td><td>@u.Age</td><td>@u.Company.Name</td></tr>
    }
</table>
@if (Model.PageViewModel.HasPreviousPage)
{
    <a asp-action="Index"
       asp-route-page="@(Model.PageViewModel.PageNumber - 1)"
       class="btn btn-outline-dark">
        <i class="glyphicon glyphicon-chevron-left"></i>
        Назад
    </a>
}
@if (Model.PageViewModel.HasNextPage)
{
    <a asp-action="Index"
       asp-route-page="@(Model.PageViewModel.PageNumber + 1)"
       class="btn btn-outline-dark">
        Вперед
        <i class="glyphicon glyphicon-chevron-right"></i>
    </a>
}

После вывода списка в таблице формируем ссылки для перехода назад и вперед. Для стилизации навигационных кнопок здесь используются шрифты awesome-font, которые подключаются в начале представления.:

Пагинация в ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850