ASP .NET - Model Binding com Web Forms - Retornando e exibindo dados - I


Vamos iniciar a nossa série de tutoriais sobre o Model Binding com ASP .NET Web Forms mostrando como acessar e exibir dados através do método SelectMethod onde vamos fornecer a lógica para a obtenção dos dados.

Nota: Esta série de tutoriais sobre o Model Binding em ASP .NET Web Forms foi baseada na série original  Tutorial Series on Model Binding with ASP.NET Web Forms com alguns ajustes e adaptações e portada para linguagem VB .NET.

Neste tutorial você vai aprender a:

Recursos usados neste tutorial:

Criando a Solução no Visual Studio

Abra o Microsoft Visual Studio Express 2012 for Web e clique em New Project;

Selecione a linguagem Visual Basic -> Web e o template ASP .NET Web Forms Application informando o nome EscolaMacoratti e clicando no botão OK;

Na janela Solution Explorer você poderá ver a solução e o projeto criado com o nome EscolaMacoratti;

O projeto já foi criado com uma estrutura básica contendo pastas e arquivos padrões alguns dos quais iremos modificar para alterar a aparência do site. (Aqui você pode ficar a vontade e criar o seu próprio leiaute)

Abra o arquivo da master page Site.Master e altere o código que exibe o texto your logo here substituindo por outro texto ou por uma imagem como foi feito no código abaixo:

....
  <p class="site-title">
     <a runat="server" href="http://www.macoratti.net"><img src="Images/macoratti.png" /></a>
  </p>
....

Ainda no arquivo Site.Master altere os links de navegação que aparecem no cabeçalho da página. Remova o link para a página Contact e inclua um link para a página Alunos.aspx que iremos criar mais adiante:

   <nav>
       <ul id="menu">
            <li><a runat="server" href="~/">Home</a></li>
            <li><a runat="server" href="~/About.aspx">Sobre</a></li>
            <li><a id="A1" runat="server" href="~/Alunos.aspx">Alunos</a></li>
       </ul>
   </nav>

Abra a página About.aspx e altere o seu conteúdo conforme mostra o código a seguir:

<%@ Page Title="About" Language="VB" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="About.aspx.vb" Inherits="EscolaMacoratti.About" %>
<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
    <hgroup class="title">
        <h2>EscolaMacoratti</h2>
    </hgroup>
    <article>
        <p>        
            Para saber mais sobre ASP .NET visite : <a href="http://www.macoratti.net">Macoratti .net</a>
        </p>
    </article>
</asp:Content>

Altere também o conteúdo da página Default.aspx conforme o código abaixo:

<%@ Page Title="Home Page" Language="VB" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.vb" Inherits="EscolaMacoratti._Default" %>
<asp:Content runat="server" ID="FeaturedContent" ContentPlaceHolderID="FeaturedContent">
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h2>EscolaMacoratti</h2>
            </hgroup>
            <p>
                Para aprender mais sobre ASP.NET, visite <a href="http://www.macoratti.nett" title="Macoratti .net">http://www.macoratti.net</a>.
            </p>
        </div>
    </section>
</asp:Content>
<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
</asp:Content>

Executando o projeto neste momento iremos obter o seguinte resultado:

1- Pagina Default.aspx

2- Página About.aspx

Vamos agora incluir uma nova página para exibir os dados dos alunos da escola.

No menu PROJECT clique em Add New Item;

A seguir selecione Visual Basic -> Web e o template Web Form using Master Page e informe o nome Alunos.aspx clicando no botão Add;

Na próxima janela selecione a master page Site.Master e clique em OK;

Criando o modelo de dados e o banco de dados

Vamos agora usar os recursos do Code First Migrations para criar os objetos e as tabelas correspondentes do banco de dados. Essas tabelas irão armazenar informações sobre os alunos e seus cursos.

O que vem a ser o recurso Migrations ?

O Code First (do Entity Framework) funciona muito bem e é muito fácil de usar. Mas ele não tem uma característica importante, o tratamento das alterações do esquema de banco de dados (adicionar/remover/modificar colunas, tipos de dados, restrições, etc.) relacionadas com as alterações feitas no modelo de entidades.

Usando o Code First quando você altera o modelo de entidades gerado você tem que recriar o seu banco de dados novamente acarretando assim perda dos dados ou tem que gerar scripts SQL para manualmente refletir as alterações no banco de dados se não quiser perder os dados.

O Migrations veio para suprir a falta deste recurso no Code First.

Usando o recurso MIgrations do Entity Framework podemos realizar alterações em nosso modelo de entidades e ter a atualização automática do banco de dados refletindo essas mudanças.

Através do Code First podemos gerar de forma automática o nosso modelo de dados a partir do modelo de entidades e com o Migrations podemos gerar atualizações no banco de dados refletindo as alterações porventura feitas no modelo de entidades.

Vamos criar uma nova pasta no projeto. Clique com o botão direito do mouse sobre o projeto e selecione Add -> New Folder informando o nome Models;

Agora clique com o botão direito sobre a pasta Models e selecione Add -> Class e informe o nome EscolaModel.vb para o arquivo.

A seguir vamos definir neste arquivo as classes que representam o nosso domínio - Uma escola , alunos, cursos, matriculas, etc..

Dessa forma vamos criar as classes:

Abaixo temos o código no arquivo EscolaModel.vb:

Imports System.Collections.Generic
Imports System.Data.Entity
Imports System.ComponentModel.DataAnnotations

Public Class EscolaContext
                  Inherits DbContext

    Public Property Alunos() As DbSet(Of Aluno)
    Public Property Matriculas() As DbSet(Of Matricula)
    Public Property Cursos() As DbSet(Of Curso)
End Class

Public Class Aluno
    <Key, Display(Name:="ID")> _
    <ScaffoldColumn(False)> _
    Public Property AlunoID() As Integer

    <Required, StringLength(40), Display(Name:="Sobrenome")> _
    Public Property Sobrenome() As String

    <Required, StringLength(20), Display(Name:="Nome")> _
    Public Property Nome() As String

    <EnumDataType(GetType(AnoAcademico)), Display(Name:="Ano Acadêmico")> _
    Public Property Ano() As AnoAcademico

    Public Overridable Property Matriculas() As ICollection(Of Matricula)
End Class

Public Class Matricula
    <Key> _
    Public Property MatriculaID() As Integer
    Public Property CursoID() As Integer
    Public Property AlunoID() As Integer
    Public Property Grau() As System.Nullable(Of Decimal)
    Public Overridable Property Curso() As Curso
    Public Overridable Property Aluno() As Aluno
End Class

Public Class Curso
    <Key> _
    Public Property CursoID() As Integer
    Public Property Titulo() As String
    Public Property Creditos() As Integer
    Public Overridable Property Matriculas() As ICollection(Of Matricula)
End Class

Public Enum AnoAcademico
    Calouro
    Segundanista
    Junior
    Senior
End Enum

Salve o arquivo.

Se você está estranhando termos criado as classes em um único arquivo isso esta correto mas você também pode criar cada classe em um arquivo separado.

A classe EscolaContext deriva de DbContext, que gerencia a conexão com o banco e as mudanças nos dados.

Na classe Aluno, observe os atributos que foram aplicados ao nome, sobrenome e propriedades do ano. Esses atributos serão utilizados para validação de dados. Para simplificar o código para o nosso projeto, apenas essas propriedades foram marcados com os atributos de validação de dados.

Em um projeto real, você aplicaria atributos de validação para todas as propriedades que necessitam de dados validados, como propriedades das classes de Matricula e Curso.

Agora vamos usar o recurso Code Firs Migrations do Entity Framework para definir um banco de dados com base nas classes que acabamos de criar.

Clique no menu TOOLS -> Library Package Manager -> Package Manager Console:

Na janela do Package Manager Console, digite o comando : Enable-migrations:

Observe que um novo arquivo chamado Configuration.cs foi criado. No Visual Studio, este arquivo é aberto automaticamente após sua criação como vemos na figura acima.

A classe de configuração contém um método Seed que permite que você preencha previamente as tabelas de banco de dados com dados de teste.

Adicione o seguinte código ao método Seed :

 Protected Overrides Sub Seed(context As EscolaContext)

            context.Alunos.AddOrUpdate(New Aluno() With {.Nome = "Jefferson", .Sobrenome = "Andre Bueno", .Ano = AnoAcademico.Segundanista _
                                    }, New Aluno() With {.Nome = "Janice", .Sobrenome = "Raquel Siqueira", .Ano = AnoAcademico.Segundanista _
                                    }, New Aluno() With {.Nome = "Artur", .Sobrenome = "Geraldes", .Ano = AnoAcademico.Calouro _
                                    }, New Aluno() With {.Nome = "Paula", .Sobrenome = "Melado", .Ano = AnoAcademico.Calouro _
                                    }, New Aluno() With {.Nome = "Yan", .Sobrenome = "Li", .Ano = AnoAcademico.Junior _
                                    }, New Aluno() With {.Nome = "Yuri", .Sobrenome = "Siqueira Filho", .Ano = AnoAcademico.Junior _
                                    }, New Aluno() With {.Nome = "Laura", .Sobrenome = "Mendonça", .Ano = AnoAcademico.Senior _
                                    }, New Aluno() With {.Nome = "Jose Carlos", .Sobrenome = "Macoratti", .Ano = AnoAcademico.Senior})

            context.SaveChanges()

            context.Cursos.AddOrUpdate(New Curso() With {.Titulo = "Química", .Creditos = 3 _
                                    }, New Curso() With {.Titulo = "Engenharia", .Creditos = 3 _
                                    }, New Curso() With {.Titulo = "Engenharia", .Creditos = 3 _
                                    }, New Curso() With {.Titulo = "Cálculo", .Creditos = 4 _
                                    }, New Curso() With {.Titulo = "Trigonometria", .Creditos = 4 _
                                    }, New Curso() With {.Titulo = "História", .Creditos = 3 _
                                    }, New Curso() With {.Titulo = "Literatura", .Creditos = 4})

            context.SaveChanges()

            context.Matriculas.AddOrUpdate(New Matricula() With {.AlunoID = 1, .CursoID = 1, .Grau = 1 _
                                        }, New Matricula() With {.AlunoID = 1, .CursoID = 2, .Grau = 3 _
                                        }, New Matricula() With {.AlunoID = 1, .CursoID = 3, .Grau = 1 _
                                        }, New Matricula() With {.AlunoID = 2, .CursoID = 4, .Grau = 2 _
                                        }, New Matricula() With {.AlunoID = 2, .CursoID = 5, .Grau = 4 _
                                        }, New Matricula() With {.AlunoID = 2, .CursoID = 6, .Grau = 4 _
                                        }, New Matricula() With {.AlunoID = 3, .CursoID = 1}, _
                                           New Matricula() With {.AlunoID = 4, .CursoID = 1}, _
                                           New Matricula() With {.AlunoID = 4, .CursoID = 2, .Grau = 4 _
                                        }, New Matricula() With {.AlunoID = 5, .CursoID = 3, .Grau = 3 _
                                        }, New Matricula() With {.AlunoID = 6, .CursoID = 4}, _
                                           New Matricula() With {.AlunoID = 7, .CursoID = 5, .Grau = 2})
            context.SaveChanges()
        End Sub
    End Class

Salve o arquivo.

Na janela do Package Manager Console digite o comando: add-migration initial

Será aberta no VS a classe Initial mostrando as mudanças a que o modelo será submetido. Você pode incluir mudanças adicionais ao modelo e gerar novamente o arquivo.

Observe que as tabelas Alunos e Cursos foram geradas com o nome Alunoes e Cursoes. Isso ocorre porque em inglês forma-se o plural acrescentando-se 'es' ao final da palavra terminada em consoante para palavras terminadas com o. Se você desejar pode corrigir isso no código gerado; eu vou aceitar os nomes padrões.

Obs: Você pode ver os arquivos gerados pelos comandos do Migrations na pasta Migrations que foi criada no projeto.

Se você receber uma exceção ao executar esse comando, é possível que os valores para AlunoID e CursoID mudaram a partir dos valores no método Seed. Abra as tabelas no banco de dados e encontre os valores existentes para AlunoID e CursoID e adicione esses valores no código para gerar a tabela Matriculas.

Para gerar as tabelas e o banco de dados execute o comando update-database janela do Package Manager Console:

Vamos verificar na janela DataBase Explorer se o banco de dados e as tabelas foram realmente criadas.

Abra a janela DataBase Explorer clique com o botão direito do mouse sobre Data Connections e a seguir em Add Connection;

Na janela a seguir em Server Name digite .\sqlexpress (se você estiver usando o SQL Server Express Local) e clique na caixa de seleção para exibir os banco de dados existentes;

Verifique o banco : \sqlexpress.Escola_Macoratti.Macoratti.Models.EscolaContext.dbo

Expandindo os objetos Tables veremos as tabelas criadas conforme definidas no EscolaModel:

Clicando na tabela Alunoes com o botão direito do mouse e selecionando a opção Show Table Data iremos visualizar os dados que foram inseridos na tabela:

Temos assim o banco de dados e as tabelas criadas e preenchidas com os dados para testes a partir do nosso modelo usando Code First Migrations

Exibindo os dados dos alunos e tabelas relacionadas

Agora que já temos o banco de dados e as tabelas criadas com os dados estamos prontos para exibir os dados em nossa página Alunos.aspx. Vamos usar um controle GridView para exibir os dados em colunas e linhas.

Abra a página Alunos.aspx e localize o placeholder MainContent. Dentro dele inclua o controle GridView conforme mostra o código abaixo:

<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="Alunos.aspx.vb" Inherits="Escola_Macoratti.Alunos" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
     <asp:GridView runat="server" ID="alunosGrid"
        ItemType="Escola_Macoratti.Macoratti.Models.Aluno" DataKeyNames="AlunoID" 
        SelectMethod="alunosGrid_GetDados"
        AutoGenerateColumns="false">
        <Columns>
            <asp:DynamicField DataField="AlunoID" />
            <asp:DynamicField DataField="Sobrenome" />
            <asp:DynamicField DataField="Nome" />
            <asp:DynamicField DataField="Ano" />          
            <asp:TemplateField HeaderText="Total de Créditos">
              <ItemTemplate>
                <asp:Label ID="Label1" Text="<%# Item.Matriculas.Sum(Function(en) en.Curso.Creditos)%>" 
                    runat="server" />
              </ItemTemplate>
            </asp:TemplateField>        
        </Columns>
    </asp:GridView>
</asp:Content>

Temos neste código alguns conceitos importantes, note que há um valor definido para a propriedade SelectMethod no elemento GridView. Este valor especifica o nome do método que é utilizado para a seleção dos dados para o GridView. Vamos criar este método logo a seguir.

Note também que a propriedade ItemType está definida para a classe Aluno. Ao definir este valor, você pode se referir às propriedades dessa classe no código de marcação. Por exemplo, a classe Aluno contém uma coleção chamada Matriculas. Podemos usar Item.Matriculas para recuperar essa coleção e, em seguida, usar a sintaxe LINQ para recuperar a soma dos créditos inscritos para cada aluno.

No code-behind, precisamos adicionar o método que é especificado para o valor SelectMethod. Abra o arquivo Aluno.aspx.vb e adicione a declaração aos namespaces Escola_Macoratti.Macoratti.Models e System.Data.Entity.

A seguir inclua o código abaixo :

Imports System.Data.Entity
Imports Escola_Macoratti.Macoratti.Models

Public Class Alunos
    Inherits System.Web.UI.Page

    Public Function alunosGrid_GetDados() As IQueryable(Of Aluno)
        Dim db As New EscolaContext()
        Dim consulta = db.Alunos.Include(Function(s) s.Matriculas.Select(Function(e) e.Curso))
        Return consulta
    End Function

End Class

Observe que o nome do método (alunosGrid_GetDados) coincide com o nome que foi fornecido para SelectMethd no GridView.

Na consulta LINQ a cláusula Include melhora o desempenho da consulta, mas não é essencial para ela funcionar.

Sem a cláusula Include, os dados seriam recuperados usando lazy loading, que envolve o envio de uma consulta separada para o banco de dados cada vez que os dados relacionados são recuperados.

Ao usar a cláusula Include, os dados são recuperados usando o eager loading, o que significa que todos os dados relacionados são recuperados através de uma única consulta de banco de dados.

Nos casos em que a maioria dos dados relacionados não serão usados, usar eager loading pode ser menos eficiente, porque mais dados são recuperados. No entanto, neste caso, a carga antecipada proporciona um desempenho melhor , pois os dados relacionados são apresentados para cada registro.

Por padrão, os dados são classificados pelos valores da propriedade marcada como a chave. Você pode adicionar uma cláusula OrderBy para especificar um valor diferente para a classificação.

Neste exemplo, a propriedade padrão AlunoID é usada para classificação. Na classificação, paginação, e filtragem de dados você vai permitir que o usuário selecione uma coluna para ordenação.

Execute o aplicativo web e navegue para a página de Alunos. A página exibe as seguintes informações do aluno :

Geração automática de métodos do Model Binding

Ao definir os valores para as propriedades SelectMethod, UpdateMethod, InsertMethod, ou DeleteMethod no código de marcação, você pode selecionar a opção Create New Method.

O Visual Studio não só ira criar um método no code-behind com a assinatura propriamente dita, mas também irá gerar o código de implementação para ajudá-lo a realizar a operação.

Se você definir a propriedade ItemType primeiro antes de usar o recurso de geração automática de código, o código gerado vai usar esse tipo para as operações. Por exemplo, ao definir a propriedade UpdateMethod, o seguinte código é gerado automaticamente:

 ' The id parameter name should match the DataKeyNames value set on the control
    Public Sub alunosGrid_UpdateItem(ByVal id As Integer)
        Dim item As Escola_Macoratti.Macoratti.Models.Aluno = Nothing
        ' Load the item here, e.g. item = MyDataLayer.Find(id)
        If item Is Nothing Then
            ' The item wasn't found
            ModelState.AddModelError("", String.Format("Item with id {0} was not found", id))
            Return
        End If
        TryUpdateModel(item)
        If ModelState.IsValid Then
            ' Save changes here, e.g. MyDataLayer.SaveChanges()
        End If
    End Sub

Neste tutorial criamos as classes do modelo de dados e geramos o banco de dados e as tabelas a partir destas classes. Preenchemos as tabelas com dados de testes e usamos o recurso Model Binding para retornar informações do banco de dados e exibi-los no GridView.

No próximo tutorial iremos implementar os métodos para atualizar, deletar e adicionar novos dados.

Referências:


José Carlos Macoratti