ASP .NET Core 3.1 -  Implementando a Paginação e o Filtro de dados


Neste artigo vou mostrar como podemos implementar a paginação e o filtro de dados em uma aplicação ASP .NET Core MVC na versão 3.1.

Existem muitas abordagens que podemos usar para implementar a paginação e o filtro de dados em uma aplicação ASP .NET Core MVC.

Podemos fazer a implementação usando apenas os recursos da ASP .NET Core por nossa conta via código C#.  Essa abordagem é a mais trabalhosa mas tem a vantagem de não depender de pacotes de terceiros.

A outra abordagem é usar um pacote de terceiros, é mais fácil e rápido mas como desvantagem seu projeto passa a depender de um dependência externa. Nada contra, mas se o pacote for gratuito lembre-se que o desenvolvedor pode não atualizar o pacote na mesma velocidade que o framework é atualizado e isso pode quebrar sua aplicação.

Neste artigo eu vou usar o pacote ReflectionIT.Mvc.Paging que é distribuído via Nuget e que a documentação (muito fraca por sinal) pode ser consultada no GitHub: https://github.com/sonnemaf/ReflectionIT.Mvc.Paging

O objetivo é mostrar mais uma opção para realizar a paginação e o filtro de dados.

Assim vou começar criando uma aplicação ASP .NET Core MVC com .NET Core 3.1 e a seguir vou implementar a paginação e o filtro de dados usando os recursos deste pacote.

recursos usados:

Criando o projeto inicial no VS 2019

Abra o VS 2019 Community e crie um novo projeto via menu File-> New Project;

Selecione o template ASP .NET Core Web Application, e, Informe o nome da solução IdentityTotal (ou outro nome a seu gosto) e o nome do projeto FuncionariosWeb.

A seguir selecione .NET Core e ASP .NET Core 3.1 e marque o template Web Application e as configurações conforme figura abaixo:

Observe que não vamos definir agora a autenticação em nosso projeto vamos fazer isso depois.

Depois que o projeto foi criado, precisamos adicionar a referência aos seguintes pacotes:

Noata:  Para instalar use o comando Install-Package <nome> -version 3.1.3

Esses pacotes são necessários para realizarmos o acesso ao banco de dados SQL Server e aplicarmos o Migrations ao modelo de domínio e assim gerar o banco de dados e as tabelas.

Definindo o modelo de domínio

Na pasta Models do projeto crie a classe Funcionario e a enumeração Departamento:

using System.ComponentModel.DataAnnotations;
namespace FuncionariosWeb.Models
{
    public class Funcionario
    {
        public int FuncionarioId { get; set; }
        [Required, MaxLength(80, ErrorMessage = "Nome não pode exceder 80 caracteres")]
        public string Nome { get; set; }
        [RegularExpression(@"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
        ErrorMessage = "Email com formato inválido")]
        [Required]
        public string Email { get; set; }

        public Departamento? Departamento { get; set; }
    }
}

Enumeração Departamento:

namespace FuncionariosWeb.Models
{
    public enum Departamento
    {
        TI,
        RH,
        Contabilidade,
        Marketing,
        Vendas
    }
}

Criando e registrando o contexto e aplicando o Migrations

Agora crie uma pasta Context no projeto e nesta pasta crie o arquivo AppDbContext:

using FuncionariosWeb.Models;
using Microsoft.EntityFrameworkCore;
namespace FuncionariosWeb.Context
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options)
      : base(options)
        { }
        public DbSet<Funcionario> Funcionarios { get; set; }
    }
}
No método ConfigureServices do arquivo Startup registre o serviço do contexto:
...
  public void ConfigureServices(IServiceCollection services)
   {
            services.AddDbContext<AppDbContext>(
                 options => options.
                 UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddControllersWithViews();
   }
...
E no arquivo appsettings.json inclua a string de conexão com o banco de dados usando sua instância local:
{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=Macoratti;Initial Catalog=FuncionariosDBWeb;Integrated Security=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Para criar o banco de dados e a tabela com as configurações definidas abra uma janela do Package Manager Console e digite o comando:

add-migration Inicial

Isso vai gerar o script de migração na pasta Migrations que será criada no projeto. Não havendo nenhum erro então emita o comando :

update-database

Isso vai criar o banco de dados e as tabelas no SQL Server.

Nota: Para alimentar a tabela com dados você pode usar a classe de Contexto - AppDbContext e definir a sobrescrita do método OnModelCreating :

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Funcionario>().HasData(
            new Funcionario
            {
                FuncionarioId = 1,
                Nome = "Macoratti",
                Departamento = Departamento.TI,
                Email = "macoratti@yahoo.com"
            });
        }

Nota:  Eu vou incluir mais alguns dados para realizar os testes.

Criando o controlador

Agora vamos criar o controlador para gerenciar as informações dos funcionários e fazer o CRUD.

Clique com o botão direito do mouse sobre a pasta Controllers e a seguir clique em Add->Controller;

A seguir escolha a opção MVC Controller with views, using Entity Framework e clique em Add;

Na janela a seguir informe o modelo Funcionario, o data context AppDbContext e informe o nome FuncionariosController e clique em Add:

Ao final será criado o controlador e as views.

Ajustando a view _Layout.cshtml para ter um link para exibir o acessso aos funcionários temos:

..

<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
 <ul class="navbar-nav flex-grow-1">
   <li class="nav-item">
      <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
    </li>
    <li class="nav-item">
  <a class="nav-link text-dark" asp-area="" asp-controller="Funcionarios" asp-action="Index">Funcionarios</a>       
</li>
</ul>
</div>
..

Agora é só alegria...

Executando o projeto teremos:

Pronto, já temos nossa aplicação ASP .NET Core MVC e agora vamos implementar a paginação e filtro de dados.

Implementando a Paginação,a ordenação e o Filtro de dados

Vamos incluir o pacote ReflectionIT.Mvc.Paging no projeto. Podemos fazer isso via janela do Package Manager Console digitando:  Install-Package ReflextionIT,Mvc.Paging -version 4.0.0 ou via menu Tools do VS 2019.

A seguir vamos definir o serviço de paginação no arquivo Startup no método ConfigureServices:

 public void ConfigureServices(IServiceCollection services)
 {
            services.AddDbContext<AppDbContext>(
                 options => options.
                 UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddControllersWithViews();
            services.AddPaging(options => {
                options.ViewName = "Bootstrap4";
                options.PageParameterName = "pageindex";
            });
 }

Após isso vamos alterar o método Action usado para gerar a View onde vamos aplicar a paginação.

Vamos abrir o controlador FuncionariosController e alterar o método Action Index conforme abaixo:

        public async Task<IActionResult> Index(string filter, int pageindex = 1, string sort = "Nome")
        {
            var resultado = _context.Funcionarios.AsNoTracking()
                                        .AsQueryable();

            if (!string.IsNullOrWhiteSpace(filter))
            {
                resultado = resultado.Where(p => p.Nome.Contains(filter));
            }
            var model = await PagingList.CreateAsync(resultado, 5, pageindex, sort, "Nome");
            model.RouteValue = new RouteValueDictionary { { "filter", filter } };
            return View(model);
        }

Neste código definimos o filtro, a paginação e a ordenação pela propriedade Nome.

Alteramos a consulta incluindo um AsQueryable() para que possamos aplicar uma cláusula Where à consulta quando o filtro não for null.

Esta consulta é usada para criar o PagingList onde definimos o tamanho da página com igual a 5 , definindo a ordenação pela coluna Nome.

A seguir definimos a rota de dados para gerar a URL onde criamos uma instância de RouteValueDictionary que representa uma coleção de pares chave/valor que não diferenciam maiúsculas de minúsculas.

Agora precisamos ajustar a view Index.cshtml conforme a seguir:

@model ReflectionIT.Mvc.Paging.PagingList<FuncionariosWeb.Models.Funcionario>
@{
    ViewData["Title"] = "Index";
}
<p>
    <a asp-action="Create">Criar Novo</a>
</p>
<form method="get" class="form-inline">
     <input name="filter" class="form-control" placeholder="filtro"
              value="@Model.RouteValue["Filter"]" />
     <button type="submit" class="btn btn-info">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span> Procurar  
</button>
</form>
Total funcionários : @Model.TotalRecordCount
<div>
    <vc:pager paging-list="@Model"></vc:pager>
</div>
<table class="table">
    <thead>
        <tr>
            <th>@Html.SortableHeaderFor(model => model.Nome, "Nome")</th>
             <th>@Html.DisplayNameFor(model => model.Email)</th>
            <th>@Html.DisplayNameFor(model => model.Departamento)</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>@Html.DisplayFor(modelItem => item.Nome)</td>
                <td>@Html.DisplayFor(modelItem => item.Email) </td>
                <td>@Html.DisplayFor(modelItem => item.Departamento)</td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.FuncionarioId">Edita</a> |
                    <a asp-action="Details" asp-route-id="@item.FuncionarioId">Detalhe</a> |
                    <a asp-action="Delete" asp-route-id="@item.FuncionarioId">Deleta</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Alteramos o tipo do model para ReflectionIT.Mvc.Paging.PagingList  e definimos o paginador usando um View Component que vai permitir navegar pelos registros.

Criamos o formulário para definir a caixa de texto e o botão para filtrar os dados com base no filtro passando via parãmetro.

No cabeçalho que exibe a coluna Nome usamos o SortableHeaderFor para destacar a coluna pela qual estamos ordenando.

Para concluir temos que incluir no arquivo _ViewImports.cshtml a referência ao namespace e à tagHelper:

@using FuncionariosWeb
@using ReflectionIT.Mvc.Paging
@using FuncionariosWeb.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, ReflectionIT.Mvc.Paging

Executando o projeto teremos o resultado a seguir:

O projeto completo aqui:   IdentityTotal_Paginacao.zip (sem as referências)

"Foge também das paixões da mocidade; e segue a justiça, a fé, o amor, e a paz com os que, com um coração puro, invocam o Senhor."
2 Timóteo 2:22

Referências:


José Carlos Macoratti