Entity Framework Core - Paginação de dados


 Neste tutorial vou mostrar uma forma simples de realizar a paginação de dados usando o EF Core.


Para mostrar de forma prática a paginação vamos criar um projeto ASP .NET Core MVC que vai acessar a tabela Products do banco de dados Northwind.mdf e paginar os dados.

Criando o projeto no VS 2019 Community

Abra no VS 2019 e clique em New Project;

A seguir selecione o template ASP .NET Core Web App(Model-View-Controller) com o nome Paginacao.

No projeto criado crie a pasta Paginacao e nesta pasta vamos definir 3 classes :

1- PagedResultBase - Classe base abstrata para resultados paginados, usada em views e Views Componentes;

using System;
namespace PaginacaoWeb.Paginacao
{
    public abstract class PagedResultBase
    {
        public int CurrentPage { get; set; }
        public int PageCount { get; set; }
        public int PageSize { get; set; }
        public int RowCount { get; set; }
        public int FirstRowOnPage
        {
            get { return (CurrentPage - 1) * PageSize + 1; }
        }
        public int LastRowOnPage
        {
            get { return Math.Min(CurrentPage * PageSize, RowCount); }
        }
    }
}

Esta classe abstrata é a classe base que iremos usar para implementar a paginação.

2- PagedResult<T> - Classe fortemente tipada para resultados e propriedades do conjunto de resultados.

using System.Collections.Generic;
namespace PaginacaoWeb.Paginacao
{
    public class PagedResult<T> : PagedResultBase where T : class
    {
        public IList<T> Results { get; set; }
        public PagedResult()
        {
            Results = new List<T>();
        }
    }
}

A classe PagedResult herda da classe PagedResultBase.

3- Paginacao - Classe que define o método de extensão GetPaged<T> que implementa a paginação.

using System;
using System.Linq;
namespace PaginacaoWeb.Paginacao
{
    public static class Paginacao
    {
        public static PagedResult<T> GetPaged<T>(this IQueryable<T> query,
                                                     int page, int pageSize) where T : class
        {  
            var result = new PagedResult<T>();
            result.CurrentPage = page;
            result.PageSize = pageSize;
            result.RowCount = query.Count();
            var pageCount = (double)result.RowCount / pageSize;
            result.PageCount = (int)Math.Ceiling(pageCount);
            var skip = (page - 1) * pageSize;
            result.Results = query.Skip(skip).Take(pageSize).ToList();
            return result;
        }
    }
}

Este é o método de extensão para IQueryable<T> que retorna uma página de resultados e alguns números que descrevem o conjunto de resultados.

Na pasta Models vamos criar a classe Product e a classe de contexto AppDbContext:

1- Product  - Representa a o modelo de entidade que iremos mapear para acessar a tabela Products do Northwind.

public class Product
    {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public decimal UnitPrice { get; set; }
    }

2- AppDbContext - A classe de contexto que herda de DbContext:

using Microsoft.EntityFrameworkCore;
namespace PaginacaoWeb.Models
{
    public class AppDbContext : DbContext
    {
        public DbSet<Product> Products { get; set; }
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {
        }
    }
}

Agora vamos configurar o contexto como um serviço no arquivo Startup:

 public void ConfigureServices(IServiceCollection services)
 {
            services.AddDbContext<AppDbContext>(options => 
               options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddControllersWithViews();
 }

E vamos definir a string de conexão no arquivo appsettings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=\\sqlexpress;Initial Catalog=Northwind;Integrated Security=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Agora no controlador HomeController vamos incluir o código abaixo:

using Microsoft.AspNetCore.Mvc;
using PaginacaoWeb.Models;
using PaginacaoWeb.Paginacao;
using System;
namespace PaginacaoWeb.Controllers
{
    public class HomeController : Controller
    {
        private readonly AppDbContext _ctx;
        public HomeController(AppDbContext ctx)
        {
            _ctx = ctx ?? throw new ArgumentNullException(nameof(ctx));
        }
        public IActionResult Index(int page = 1)
        {
            var dataProducts = _ctx.Products.GetPaged(page, 10);
            return View(dataProducts);
        }
        public IActionResult Privacy()
        {
            return View();
        }
    }
}

No controlador injetamos uma instância do contexto e usamos o método de extensão GetPaged() para realizar a paginação dos dados.

Agora só falta definir o código da view Index.cshtml na pasta /Views/Home:

@model PaginacaoWeb.Paginacao.PagedResult<Product>
<table class="table table-bordered table-hover">
    <thead>
        <tr>
            <th>#</th>
            <th>ProductId</th>
            <th>ProductName</th>
            <th>Unit Price</th>
        </tr>
    </thead>
    <tbody>
        @{ var i = 1; }
        @foreach (var product in Model.Results)
        {
            var rowNo = (Model.CurrentPage - 1) * Model.PageSize + i;
            i++;
            <tr>
                <td>@rowNo</td>
                <td>@product.ProductID</td>
                <td>@product.ProductName</td>
                <td>@product.UnitPrice</td>
            </tr>
        }
    </tbody>
</table>
@for (var p = 1; p <= Model.PageCount; p++)
{
    <a href="@Url.Action("Index", new { page = p })" class="btn btn-default">@p</a>
}

Executando o projeto teremos o seguinte resultado:

   

Simples e eficiente...

Pegue o projeto aqui :  EFCorePaginacao.zip (sem as referências)

E estamos conversados...

"Bendirei o Senhor, que me aconselha; na escura noite o meu coração me ensina!"
Salmos 16:7

Referências:


José Carlos Macoratti