DEV Community

Masui Masanori
Masui Masanori

Posted on

[ASP.NET Core] Using X.PagedList

Intro

This time, I will try X.PagedList for pagenation for Razor.

Environments

  • .NET ver.6.0.100-preview.2.21155.3
  • Microsoft.EntityFrameworkCore ver.6.0.0-preview.2.21154.2
  • Npgsql.EntityFrameworkCore.PostgreSQL ver.6.0.0-preview2
  • NLog.Web.AspNetCore ver.4.11.0
  • Microsoft.EntityFrameworkCore.Design ver.6.0.0-preview.2.21154.2
  • X.PagedList ver.8.0.7
  • X.PagedList.Mvc.Core ver.8.0.7

package.json (To use bootstrap)

{
    "browserslist": [
        "last 2 version"
    ],
    "dependencies": {
        "autoprefixer": "^10.2.5",
        "bootstrap": "^4.6.0",
        "postcss": "^8.2.8",
        "postcss-cli": "^8.3.1",
        "postcss-import": "^14.0.0"
    },
    "scripts": {
        "css": "npx postcss postcss/*.css -c postcss.config.js -d wwwroot/css -w"
    }
}
Enter fullscreen mode Exit fullscreen mode

Base project

Except the package versions, I used this project.

Use X.PagedList

According to the Example, I just need 1.getting "IQueryable" items, 2.converting to IPagedList, and 3.setting into "@Html.PagedListPager".

BookService.cs

using System.Linq;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BookStoreSample.Applications;
using BookStoreSample.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace BookStoreSample.Books
{
    public class BookService: IBookService
    {
...
        public IQueryable<Book> GetBooks(int? authorId)
        {
            if(authorId != null && authorId > 0)
            {
                return context.Books.Include(b => b.Author)
                    .Include(b => b.Genre)
                    .Where(b => b.AuthorId == authorId);    
            }
            return context.Books.Include(b => b.Author)
                .Include(b => b.Genre);
        }
        public async Task<List<Author>> GetAuthorsAsync()
        {
            return await context.Authors.ToListAsync();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

HomeController.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using BookStoreSample.Applications;
using BookStoreSample.Books;
using BookStoreSample.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using X.PagedList;

namespace BookStoreSample.Controllers
{
    public class HomeController: Controller
    {
...
        [Route("Pages/Razor")]
        public IActionResult OpenRazorPage(int? Page)
        {
            // Get items as IQueryable<Book> and convert to IPagedList<Book>
            // Page min value is 1.
            ViewData["Books"] = books.GetBooks().ToPagedList(Page ?? 1, 5);
            ViewData["Title"] = "Hello Pagenation";
            return View("Views/RazorPagination.cshtml");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

site.css

@import "../node_modules/bootstrap/dist/css/bootstrap.min.css";
#book_search_result {
    height: 70vh;
    width: 100%;
}
.book_search_result_row {
    border: 1px solid black;
    display: flex;
    flex-direction: row;
    align-items: center;
    height: 20%;
    width: 100%;
}
Enter fullscreen mode Exit fullscreen mode

RazorPagination.cshtml

@using X.PagedList;
@using X.PagedList.Mvc.Core;
@using X.PagedList.Mvc.Core.Common;
@using BookStoreSample.Controllers;
@using BookStoreSample.Models;
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers;
@{
    IPagedList<Book>? bookPages = ViewData["Books"] as IPagedList<Book>;
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewData["Title"]</title>
        <link rel="stylesheet" href="/css/site.css">
    </head>
<body>
    <div id="book_search_result">
        @if(bookPages != null && bookPages.Count > 0)
        {
            @foreach (var item in bookPages)
            {
                <div class="book_search_result_row">
                    <div>@item.Id</div>
                    <div>@item.Name</div>
                </div>
            }
        }
    </div>

    @Html.PagedListPager(bookPages, page => Url.Action("OpenRazorPage", "Home", 
        new { Page = page }),
        new PagedListRenderOptions {
            LiElementClasses = new string[] { "page-item" },
            PageClasses = new string[] { "page-link" }
    })
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

How many times are queries executed?

From output log, the answer is 4 times in total.

  1. getting all "Author" rows
  2. getting "Book" rows to show result
  3. getting "Book" count for X.PagedList
  4. getting "Book" rows for X.PagedList

2.and 4. are totally same.
When their execution times are so slow, I may have to choose another way for pagination.

Use ViewModel

How about cases what using "ViewModel"?

RazorPaginationViewModel.cs

using X.PagedList;
using BookStoreSample.Models;

namespace BookStoreSample.ViewModels
{
    public record RazorPaginationViewModel
    {
        public int? Page { get; init; }
        public int? AuthorId { get; init; }
        public IPagedList<Book>? Books { get; init; } 
    }
}
Enter fullscreen mode Exit fullscreen mode

First, I added "Model.AuthorId" into the anonimous class of @Html.PagedListPager.

HomeController.cs

...
namespace BookStoreSample.Controllers
{
    public class HomeController: Controller
    {
...
        [Route("Pages/Razor")]
        public IActionResult OpenRazorPage(RazorPaginationViewModel? viewModel)
        {
            ViewData["Books"] = books.GetBooks(viewModel?.AuthorId).ToPagedList(viewModel?.Page ?? 1, 5);
            ViewData["Title"] = "Hello Pagenation";
            return View("Views/RazorPagination.cshtml");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

RazorPagination.cshtml (Failed)

@using X.PagedList;
@using X.PagedList.Mvc.Core;
@using X.PagedList.Mvc.Core.Common;
@using BookStoreSample.Books;
@using BookStoreSample.Controllers;
@using BookStoreSample.Models;
@using BookStoreSample.ViewModels;
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers;
@inject IBookService books;
@model RazorPaginationViewModel;
@{
    IPagedList<Book>? bookPages = ViewData["Books"] as IPagedList<Book>;
    List<Author> authors = await books.GetAuthorsAsync();
}
...
    <div>
        @Html.DropDownListFor(
            model => Model!.AuthorId,
            authors.Select(a => new SelectListItem{ Value = a.Id.ToString(), Text = a.Name }),
            "Author",
            new { @class = "form-control" })
    </div>
...
    <!-- Don't do this -->
    @Html.PagedListPager(bookPages, page => Url.Action("OpenRazorPage", "Home", 
        new { Page = page,
            AuthorId = Model?.AuthorId    
        }),
        new PagedListRenderOptions {
            LiElementClasses = new string[] { "page-item" },
            PageClasses = new string[] { "page-link" }
    })
...
Enter fullscreen mode Exit fullscreen mode

Model is null?

But the link of page links were like "http://localhost:5000/Pages/Razor?Page=1".
Because "Model?.AuthorId" was always null.

I think the reason is creating page links before Model instance are set.
So I set the value through "ViewData".

HomeController.cs

...
namespace BookStoreSample.Controllers
{
    public class HomeController: Controller
    {
...
        [Route("Pages/Razor")]
        public IActionResult OpenRazorPage(RazorPaginationViewModel? viewModel)
        {
            ViewData["Books"] = books.GetBooks(viewModel?.AuthorId).ToPagedList(viewModel?.Page ?? 1, 5);
            ViewData["AuthorId"] = viewModel?.AuthorId;
            ViewData["Title"] = "Hello Pagenation";
            return View("Views/RazorPagination.cshtml");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

RazorPagination.cshtml (Failed)

...
    @Html.PagedListPager(bookPages, page => Url.Action("OpenRazorPage", "Home", 
        new { Page = page,
            AuthorId = ViewData["AuthorId"]      
        }),
        new PagedListRenderOptions {
            LiElementClasses = new string[] { "page-item" },
            PageClasses = new string[] { "page-link" }
    })
...
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
sphavix profile image
sphavix

I have been looking for an article like this. I have been trying to use PageList & PagedList.Mvc in a ASP.NET Core application. I guess this will be a better solution for because I didn't know about X.PagedList. Thank you for this article.

Collapse
 
samsimonseverua profile image
Sam Severua ₿

I am getting this error
Severity Code Description Project File Line Suppression State

Error CS7069 Reference to type 'HtmlString' claims it is defined in 'System.Web', but it could not be found XXXXXX C:\XXXXXX\XXXXXX\XXXXXX\Views\Report\ListAllReports.cshtml 14 Active

I am on Visual Studio and a .net core 3.1 MVC web app