ASP .NET MVC 3 - Ordenação, Filtragem e Paginação com EF - III


Este artigo é baseado no original: Creating an Entity Framework Data Model for an ASP.NET MVC Application (1 of 10) com adaptações e pequenos ajustes feitos por mim.

Antes de prosseguir verifique se você possui os seguintes recursos instalados:

Esta é terceira parte do artigo, e, você esta chegando agora deve ler obrigatoriamente as partes anteriores:

  1. ASP .NET MVC 3 - Criando uma aplicação ASP .NET MVC3
  2. ASP .NET MVC 3 - Implementando as funcionalidades CRUD básicas com EF - II

Neste artigo vamos abordar implementar a ordenação, filtragem e paginação dos dados usando o Entity Framework 4.1 e os recursos da ASP .NET MVC 3.

Implementando a Ordenação, Filtragem e Paginação

Abra o Visual Web Developer 2010 Express Edition e no menu File clique em Open Project; a seguir selecione o projeto que já foi criado no primeiro artigo com o nome UniversidadeMacoratti e clique em OK;

A estrutura do projeto exibida na janela Solution Explorer deverá ser a seguinte:

Obs: Se você quiser continuar a partir deste artigo faça o download do projeto UniversidadeMacoratti_2.zip para abri-lo no Visual Web Developer 2010 Express Edition.

Se executarmos a aplicação e clicarmos na aba Estudantes iremos obter a página a seguir a partir da qual poderemos acionar as funcionalidades para ordenação, filtragem e paginação;

Incluindo links para ordenação nas colunas da página Index dos Estudantes

Para incluir links para ordenação nas colunas do cabeçalho da página Index vamos alterar o método Index do Controller Estudante e incluir o código necessário na view Index;

1- Incluindo a funcionalidade de ordenação no método Index;

Abra o arquivo EstudanteController.vb que esta na pasta Controller (Controller/EstudanteController.vb) e substitua o método Index abaixo:

' GET: /Estudante/

Function Index() As ViewResult

    Return View(db.Estudantes.ToList())

End Function

Pelo seguinte código:

        '
        ' GET: /Student/
        Public Function Index(ordenacaoOrdem As String)
            ViewBag.NomeOrdenacaoParm = IIf(String.IsNullOrEmpty(ordenacaoOrdem), "Nome desc", "")
            ViewBag.DataOrdenacaoParm = IIf(ordenacaoOrdem = "Data", "Data desc", "Data")
            Dim estudantes = From est In db.Estudantes
                                        Select est
            Select Case ordenacaoOrdem
                Case "Nome desc"
                    estudantes = estudantes.OrderByDescending(Function(est) est.SobreNome)
                    Exit Select
                Case "Data"
                    estudantes = estudantes.OrderBy(Function(est) est.DataMatricula)
                    Exit Select
                Case "Data desc"
                    estudantes = estudantes.OrderByDescending(Function(est) est.DataMatricula)
                    Exit Select
                Case Else
                    estudantes = estudantes.OrderBy(Function(est) est.SobreNome)
                    Exit Select
            End Select
            Return View(estudantes.ToList())
        End Function

Este código recebe um parâmetro ordenacaoOrdem a partir de uma Query String na URL, a qual é fornecida pela ASP .NET MVC como um parâmetro para o método action. O parâmetro será uma string que pode ser "Nome" ou "Data", opcionalmente seguida por um espaço e a string "desc" para especificar a ordenação decrescente.

A primeira vez que a página é requisitada, não haverá query string e os estudantes serão exibidos na ordem ascendente de sobrenome.

Quando o usuário clicar em um link da coluna para realizar a ordenação, o respectivo valor ordenacaoOrdem será fornecido para a query string.

As variáveis ViewBag são usadas de forma que a view pode configurar os hiperlinks das colunas de ordenação com os valores apropriados da query string.

Obs: ViewBag é usada para passar dados dos controllers para as views da mesma forma que ViewData. (ViewBag é um tipo dinâmico)

            ViewBag.NomeOrdenacaoParm = IIf(String.IsNullOrEmpty(ordenacaoOrdem), "Nome desc", "")
            ViewBag.DataOrdenacaoParm = IIf(ordenacaoOrdem = "Data", "Data desc", "Data")

O primeiro código define que se o parâmetro ordenacaoOrdem for null ou vazio (empty) a variável  ViewBag.NomeOrdenacaoParm deverá ser definida para "Nome desc", caso contrário deverá ser definido para uma string vazia ("").

Existem 4 possibilidades dependendo como os dados estão atualmente ordenados:

  1. Se a ordem atual for Sobrenome ascendente, o link Sobrenome deve especificar Sobrenome descendente, e  o link data de matricula deve especificar Data ascendente;
  2. Se a ordem atual for Sobrenome descendente, os links devem indicar Sobrenome ascendente e Data ascendente;
  3. Se a ordem atual for Data ascendente, os links devem indicar Sobrenome ascendente e Data descendente;
  4. Se a ordem atual for Data descendente, os links devem indicar Sobrenome ascendente e Data ascendente;

O método utiliza o LINQ to Entities para especificar a coluna a ser ordenada. O código cria uma variável IQueryable antes da instrução Select Case, modifica-a na instrução Select Case e chama o método ToList depois da instrução Select Case.

Quando você cria e modifica variáveis IQueryable, nenhuma consulta é enviada para o banco de dados. A consulta não é executada até que você  converta o objeto IQueryable em uma coleção chamando o método ToList. Por isso, este código resulta em uma única consulta que não é executada até a chamada da instrução return view.

Incluindo hiperlinks no cabeçalho das colunas da View Index para Estudantes

Na pasta Views\Estudante  abra o arquivo Index.vbhtml e substitua os elementos <tr> e <th> para o cabeçalho da linha pelo seguinte código:

<tr>

<th>

  SobreNome

</th>

<th>

  Nome

</th>

<th>

  Data da Matricula

</th>

</tr>

 

<tr>

<th>

   @Html.ActionLink("Sobrenome", "Index", New With {.ordenacaoOrdem = ViewBag.NomeOrdenacaoParm, .currentFilter = ViewBag.CurrentFilter})

</th>

<th>

   Nome

</th>

<th>

   @Html.ActionLink("Data Matrícula", "Index", New With {.ordenacaoOrdem = ViewBag.DataOrdenacaoParm, .currentFilter = ViewBag.CurrentFilter})

</th>

</tr>

 

Este código utiliza a informação da propriedade ViewBag para definir os hiperlinks com os valores das strings para a consulta.

Executando o projeto e clicando na aba Estudantes temos o seguinte resultado:

Para testar a funcionalidade basta clicar nos links Sobrenome e Data Matricula.

Incluindo uma caixa de Procura para estudantes na página Index

Para incluir a funcionalidade que permite filtrar dados dos estudantes vamos incluir um controle TextBox e um controle Button na view e fazer os ajustes correspondentes no método Index. A caixa de texto (TextBox) vai permitir que o usuário informe uma string para busca no nome e sobrenome do estudante.

Abra o arquivo EstudanteController.vb na pasta Controllers e vamos alterar o método Index para ter o seguinte código:

       ' GET: /Student/
        Public Function Index(ordenacaoOrdem As String, strCriterio As String) 
            ViewBag.NomeOrdenacaoParm = IIf(String.IsNullOrEmpty(ordenacaoOrdem), "Nome desc", "")
            ViewBag.DataOrdenacaoParm = IIf(ordenacaoOrdem = "Data", "Data desc", "Data")
            Dim estudantes = From est In db.Estudantes
                             Select est
            If Not String.IsNullOrEmpty(strCriterio) Then
                estudantes = estudantes.
                             Where(Function(est) est.SobreNome.ToUpper().Contains(strCriterio.ToUpper()) _
                             OrElse est.Nome.ToUpper().Contains(strCriterio.ToUpper()))
            End If
            Select Case ordenacaoOrdem
                Case "Nome desc"
                    estudantes = estudantes.OrderByDescending(Function(est) est.SobreNome)
                    Exit Select
                Case "Data"
                    estudantes = estudantes.OrderBy(Function(est) est.DataMatricula)
                    Exit Select
                Case "Data desc"
                    estudantes = estudantes.OrderByDescending(Function(est) est.DataMatricula)
                    Exit Select
                Case Else
                    estudantes = estudantes.OrderBy(Function(est) est.SobreNome)
                    Exit Select
            End Select
            Return View(estudantes.ToList())
        End Function

Fizemos as seguintes alterações no método Index: (as inclusões estão destacadas na cor azul)

1- Incluímos o parâmetro strCriterio do tipo string no método Index;
2- Incluímos uma cláusula Where na instrução LINQ que seleciona somente estudantes cujo nome ou sobrenome contenham a string de critério informada na caixa de texto que iremos incluir na view;

Para incluir a caixa de texto na View abra o arquivo Index.vbhtml na pasta Views\Estudantes e inclua um título, um TextBox e um Button antes a tag table conforme mostra o texto destacado no trecho de código da página Index.vbhtml abaixo:

@ModelType IEnumerable(Of UniversidadeMacoratti.UniversidadeMacoratti.Models.Estudante)

@Code

ViewData("Title") = "Estudantes"

End Code

<h2>Estudantes</h2>

<p>

@Html.ActionLink("Criar Novo", "Criar")

</p>


@
Using Html.BeginForm()

   @<p>

      Procurar por nome: @Html.TextBox("strCriterio", ViewBag.CurrentFilter) &nbsp;

       <input type="submit" value="Procurar" /></p>

  End Using
 

<table>
.....

.....

 

Execute o projeto e informe um texto como critério de busca na caixa de texto e clique no botão Procurar para verificar o resultado:

Implementando a funcionalidade de paginação

Vamos agora implementar a paginação e para isso vamos usar o componente PagedList.

Para obter o componente você pode usar o NuGet ou o próprio ambiente do Visual Web Developer ou Visual Studio via menu Tools -> Extension Manager e pesquisar pelo componente para fazer o seu download:

Se você não conseguir usando este caminho, procure no Google por PagedList e faça o download no link : http://pagedlist.codeplex.com/releases/view/31087

Apos o download clique no menu Project -> Add Reference, e, a seguir clique em Browse e selecione o local onde você instalou o componente, selecione PagedList.dll e clique em OK;

Após referenciar o componente abra o arquivo EstudanteController.vb na pasta Controllers e inclua a instrução imports para o PagedList;

Imports PagedList

A seguir vamos alterar o método Index deste arquivo conforme o código abaixo:

        ' GET: /Student/
        Public Function Index(ordenacaoOrdem As String, filtroAtual As String, strCriterio As String, pagina As System.Nullable(Of Integer)) 
            ViewBag.CurrentOrder = ordenacaoOrdem 
            ViewBag.NomeOrdenacaoParm = IIf(String.IsNullOrEmpty(ordenacaoOrdem), "Nome desc", "")
            ViewBag.DataOrdenacaoParm = IIf(ordenacaoOrdem = "Data", "Data desc", "Data")
            If Request.HttpMethod = "GET" Then 
                strCriterio = filtroAtual 
            Else pagina = 1

            ViewBag.CurrentFilter = strCriterio
            Dim estudantes = From est In db.Estudantes
                                      Select est
            If Not String.IsNullOrEmpty(strCriterio) Then
                estudantes = estudantes.
                             Where(Function(est) est.SobreNome.ToUpper().Contains(strCriterio.ToUpper()) _
                             OrElse est.Nome.ToUpper().Contains(strCriterio.ToUpper()))
            End If
            Select Case ordenacaoOrdem
                Case "Nome desc"
                    estudantes = estudantes.OrderByDescending(Function(est) est.SobreNome)
                    Exit Select
                Case "Data"
                    estudantes = estudantes.OrderBy(Function(est) est.DataMatricula)
                    Exit Select
                Case "Data desc"
                    estudantes = estudantes.OrderByDescending(Function(est) est.DataMatricula)
                    Exit Select
                Case Else
                    estudantes = estudantes.OrderBy(Function(est) est.SobreNome)
                    Exit Select
            End Select

            Dim paginaTamanho As Integer = 3
            Dim paginaNumero As Integer = (If(pagina, 1))
            Return View(estudantes.ToPagedList(paginaNumero, paginaTamanho))
        End Function

Este código inclui os seguintes parâmetros ao método Index:

Na primeira execução da página ou se o usuário clicar não clicar no link de paginação, a variável pagina será null. Se o link de paginação for clicado a variável irá conter o número da página a ser exibida.

A propriedade ViewBag fornece a view com a ordenação  atual, pois isto deve ser incluído  nos links de paginação a fim de manter mesma ordenação da paginação:

            ViewBag.CurrentOrder = ordenacaoOrdem

A outra propriedade ViewBag fornece a view com a string do filtro atual, pois esta string deve ser armazenada no TextBox quando a página for exibida. Além disso a string deve ser incluída nos links de paginação afim de manter as configurações de filtro durante a paginação.

Se a string de busca for alterada durante a paginação, a página tem que ser resetada para o valor 1, pois um novo filtro pode resultar em dados diferentes na exibição:

            If Request.HttpMethod = "GET" Then
                strCriterio = filtroAtual
            Else pagina = 1

            ViewBag.CurrentFilter = strCriterio

No final a consulta é convertida para um PagedList ao invés de ToList de forma que ele será passado para view em uma coleção que suporta a paginação:

            Dim paginaTamanho As Integer = 3
            Dim paginaIndice As Integer = (If(pagina, 1)) -1
            Return View(estudantes.ToPagedList(paginaIndice , paginaTamanho))

O método ToPageList() usa o índice da página, o qual é base zero, ao invés do número da página que é base 1. Por isso estamos subtraindo uma unidade.

Vamos agora definir os links de paginação na View Index do Estudante.

Abra o arquivo Index.vbhtml na pasta Views\Estudante\ e altere o código conforme abaixo:

@ModelType PagedList.IPagedList(Of UniversidadeMacoratti.UniversidadeMacoratti.Models.Estudante)  
@Code
    ViewData("Title") = "Estudantes"
End Code
<h2>Estudantes</h2>
<p>
    @Html.ActionLink("Criar Novo", "Criar")
</p>
@Using Html.BeginForm()
    @<p>
        Procurar por nome: @Html.TextBox("strCriterio", ViewBag.CurrentFilter) &nbsp;
        <input type="submit" value="Procurar" /></p>
End Using
<table>
      <tr>
        <th>
            @Html.ActionLink("Sobrenome", "Index", New With {.ordenacaoOrdem = ViewBag.NomeOrdenacaoParm, .currentFilter = ViewBag.CurrentFilter})
        </th>
        <th>
           Nome
        </th>
        <th>
            @Html.ActionLink("Data Matrícula", "Index", New With {.ordenacaoOrdem = ViewBag.DataOrdenacaoParm, .currentFilter = ViewBag.CurrentFilter})
        </th>
    </tr>
@For Each item In Model
    Dim currentItem = item
    @<tr>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.SobreNome)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Nome)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.DataMatricula)
        </td>
        <td>
            @Html.ActionLink("Editar", "Edit", New With {.id = currentItem.EstudanteID}) |
            @Html.ActionLink("Detalhes", "Details", New With {.id = currentItem.EstudanteID}) |
            @Html.ActionLink("Deletar", "Delete", New With {.id = currentItem.EstudanteID})
        </td>
    </tr>
Next
</table>
<div>
    Página @(IIf(Model.PageCount < Model.PageNumber, 0, Model.PageNumber))
    de @Model.PageCount
    &nbsp;
    @If Model.HasPreviousPage Then
        @Html.ActionLink("<<", "Index", New With {.pagina = 1, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
        @Html.Raw("&nbsp;")
        @Html.ActionLink("< Anterior", "Index", New With {.pagina = Model.PageNumber - 1, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
    Else
        @:<<
        @Html.Raw("&nbsp;")
        @:< Anterior
    End If
    &nbsp;
    @If Model.HasNextPage Then
        @Html.ActionLink("Próxima >", "Index", New With {.pagina = Model.PageNumber + 1, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
        @Html.Raw("&nbsp;")
        @Html.ActionLink(">>", "Index", New With {.pagina = Model.PageCount, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
    Else
        @:Próxima >
        @Html.Raw("&nbsp;")
        @:>>
    End If
</div>

O código que foi alterado esta destacado em azul.

@ModelType PagedList.IPagedList(Of UniversidadeMacoratti.UniversidadeMacoratti.Models.Estudante) 

No início da página substituímos a declaração ModelType onde ao invés da view usar o objeto List agora estamos usando o objeto PagedList.

No final da página temos a definição do código que controla a paginação.

<div>
    Página @(IIf(Model.PageCount < Model.PageNumber, 0, Model.PageNumber))
    de @Model.PageCount
    &nbsp;
    @If Model.HasPreviousPage Then
        @Html.ActionLink("<<", "Index", New With {.pagina = 1, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
        @Html.Raw("&nbsp;")
        @Html.ActionLink("< Anterior", "Index", New With {.pagina = Model.PageNumber - 1, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
    Else
        @:<<
        @Html.Raw("&nbsp;")
        @:< Anterior
    End If
    &nbsp;
    @If Model.HasNextPage Then
        @Html.ActionLink("Próxima >", "Index", New With {.pagina = Model.PageNumber + 1, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
        @Html.Raw("&nbsp;")
        @Html.ActionLink(">>", "Index", New With {.pagina = Model.PageCount, .ordenacaoOrdem = ViewBag.CurrentSort, .currentFilter = ViewBag.CurrentFilter})
    Else
        @:Próxima >
        @Html.Raw("&nbsp;")
        @:>>
    End If
</div>

Executando o projeto iremos obter:

Na base da página temos que:

Dessa forma concluímos todos os ajustes nos controllers e views para implementarmos as funcionalidades de ordenação, filtragem e paginação de dados.

Pegue o projeto completo aqui:   UniversidadeMacoratti_3.zip

"E a ninguém na terra chameis vosso pai, porque um só é o vosso Pai, o qual esta nos céus."
"Nem vos chameis mestres, porque um só é o vosso Mestre, que é o Cristo." Mateus 23:9-10

Referências:

  1. VB .NET - Implementando o relacionamento um-para-muitos

  2. Entity Framework 4.0 - Lazy Loading - Macoratti.net

  3. Entity Framework 4.0 - Carregando dados - Macoratti.net

  4. Entity Framework 4.0 - Características e operações ... - Macoratti.net

  5. Entity Framework - Mestre Detalhes - Macoratti.net

  6. VS 2010 Novidades

  7. C# - Os tipos Nullable (Tipos Anuláveis)

  8. VB .NET - Nothing , Null (DBNull.Value) e Nullabe Types

  9. Entity Framework 4.0 - Lazy Loading - Macoratti.net

  10. ASP .NET MVC - Introdução

  11. ASP .NET - Apresentando o ASP .NET MVC 3 - Macoratti.net


José Carlos Macoratti