Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Пагинация или постраничный вывод позволяет разбить набор объектов на отдельные страницы. Подобный механизм особенно удобен, если данных очень много - десятки, сотни и даже тысячи объектов. Рассмотрим пару способов, как добавить в проект постраничную навигацию.
Для работы возьмем модели 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; } } }
В итоге весь проект будет выглядеть следующим образом:
Используем все эти классы в контроллере 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, которые подключаются в начале представления.: