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

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

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

Для работы возьмем модели 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; } = new();
}

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

using Microsoft.EntityFrameworkCore;

namespace MvcApp.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();
        }
    }
}

И в файле Program.cs настроим использование этого контекста данных:

using Microsoft.EntityFrameworkCore;
using MvcApp.Models;

var builder = WebApplication.CreateBuilder(args);

string connection = "Server = (localdb)\\mssqllocaldb;Database = userstoredb;Trusted_Connection=true";
builder.Services.AddDbContext<UsersContext>(options => options.UseSqlServer(connection));

builder.Services.AddControllersWithViews();

var app = builder.Build();

app.MapDefaultControllerRoute();

app.Run();

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

namespace MvcApp.Models
{
    public class PageViewModel
    {
        public int PageNumber { get;}
        public int TotalPages { get;}
        public bool HasPreviousPage => PageNumber > 1;
        public bool HasNextPage => PageNumber < TotalPages;

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

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

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

namespace MvcApp.Models
{
    public class IndexViewModel
    {
        public IEnumerable<User> Users { get;}
        public PageViewModel PageViewModel { get;}
        public IndexViewModel(IEnumerable<User> users, PageViewModel viewModel)
        {
            Users = users;
            PageViewModel = viewModel;
        }
    }
}

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

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

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

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MvcApp.Models;

namespace MvcApp.Controllers
{
    public class HomeController : Controller
    {
        UsersContext db;
        public HomeController(UsersContext context)
        {
            db = context;
            // добавляем начальные данные
            if (!db.Companies.Any())
            {
                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(items, pageViewModel);
            return View(viewModel);
        }
    }
}

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

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

var count = await source.CountAsync();

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

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

И в конце для метода Index контроллера HomeController определим следующее представление Index.cshtml:

@using MvcApp.Models
@model IndexViewModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<style>
.glyphicon { display: inline-block; padding:0 5px;}
.glyphicon-chevron-right:after { content: "\00BB"; }
.glyphicon-chevron-left:before { content: "\00AB"; }
</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>
<p>
@if (Model.PageViewModel.HasPreviousPage)
{
    <a asp-action="Index"
       asp-route-page="@(Model.PageViewModel.PageNumber - 1)" class="glyphicon glyphicon-chevron-left">
        Назад
    </a>
}
@if (Model.PageViewModel.HasNextPage)
{
    <a asp-action="Index"
       asp-route-page="@(Model.PageViewModel.PageNumber + 1)" class="glyphicon glyphicon-chevron-right">
        Вперед
    </a>
}
</p>

После вывода списка в таблице формируем ссылки для перехода назад и вперед:

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