Entity Framework 4.1 - ASP .NET com Code-First (C#) - I


 

Em artigos anteriores eu já apresentei os novos recursos do Entity Framework 4.1 como Code-First, os recursos da nova API DbContext, as convenções padrão, os Data Annotations, etc.


Neste artigo vou criar uma aplicação ASP .NET usando grande parte dos recursos disponíveis no Entity Framework 4.1 para mostrar como usá-los em uma aplicação real.


Recursos usados

Eu vou usar os seguintes recursos:

  1. Visual Web Developer 2010
  2. Linguagem C#
  3. Versão 4.1 do Entity Framework

Obs: Você deve ter o SQL Server 2008 Express Edition instalado

Objetivos

Criar uma aplicação ASP .NET e realizar as operações CRUD usando os recursos do Entity Framework 4.1 (Code-First, API DBcontext, Data Annotations, Validações, Mapeamentos, etc.)

Criando a aplicação ASP .NET

Abra o Visual Web Developer 2010 Express Edition (a versão é gratuita) e no menu File selecione New Project;

Selecione a linguagem C# (note que você também pode escolher Visual Basic) , clique em Web e a seguir escolha o template: ASP .NET Empty Web Application

Informe o nome Escola_CRUD_EF41 e clique em OK; (observe a localização do seu projeto e o nome da solução)

Será criado um projeto vazio.

Neste exemplo eu não vou criar outros projetos na solução vou trabalhar apenas com uma solução e um projeto.

Como vamos usar os tipos a partir do EF 4.1 precisamos incluir uma referência ao assembly EntityFramework;

No menu Project selecione Add Reference e a seguir selecione a guia Browse e localize a pasta onde você instalou o Entity Framework 4.1;

A seguir selecione o assembly EntityFramework.dll e clique no botão OK;

Precisamos também incluir uma referência ao assembly do Entity Framework existente.

Repita o processo acima e selecione a guia NET e seguir procure pelo assembly System.Data.Entity selecione-o e clique em OK;

Precisamos também incluir uma referência a System.ComponentModel.DataAnnotattions;

Repita o processo acima e selecione a guia NET e seguir procure pelo assembly System.ComponentModel.DataAnnotattions selecione-o e clique em OK;

Pronto as referências já estão todas adicionadas ao projeto.

Definindo o modelo

Vamos definir um modelo muito simples usando classes. Como estamos iniciando eu vou criar classes separadas no mesmo projeto mas o mais correto seria criar um projeto separado e nele criar as classes. Vou criar uma pasta chamada Model no projeto, e nela criar as nossas classes.

Obs: Vou usar praticamente o mesmo modelo do artigo : Entity Frameweork 4 - Usando Data Annotations (VB .NET)

Clique com o botão direito sobre o nome do projeto e selecione Add -> New Folder e a seguir informe o nome: Model

Vamos iniciar criando o nosso modelo de negócio através da definição das classes do nosso domínio. Para tornar a tarefa mais suave e de fácil entendimento vou adotar um modelo simplificado que pode ser visto no diagrama de classes a seguir:

Esta modelagem não esta essencialmente correta, esta sendo usada apenas por questão didática.

O nosso modelo de negócio consiste de das classes:
  • Aluno
  • Curso

O modelo de classes representa um Curso que possui 1 ou mais alunos, e um professor que ministra um ou mais cursos.  Temos aqui uma associação que representa o relacionamento um-para-muitos.

O relacionamento um-para-muitos é usado quando uma entidade A pode se relacionar com uma  ou mais entidades B. Este relacionamento é representado pelo sinal: 1:N ou 1: * que expressa a cardinalidade do relacionamento.

A seguir com o modelo em mente vamos criar cada classe que representa as entidades acima descritas.

Clique com o botão direito do mouse sobre a pasta Model e selecione Add Class e a seguir informe o nome Aluno.cs e clique no botão Add;

A seguir defina o seguinte código para a classe Aluno:

using System.ComponentModel.DataAnnotations;

namespace Escola_CRUD_EF41.Model

{

  [Table("Aluno")]

  public class Aluno

  {

    [Key]

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]

    public int alunoCodigo { get; set; }
 

    [StringLength(50)]

    [Required(ErrorMessage="O nome do aluno é obrigatório")]

    public string alunoNome { get; set; }
 

    [StringLength(80)]

    [Required(ErrorMessage = "O email do aluno é obrigatório")]

    public string alunoEmail { get; set; }
 

    [Required , StringLength(1, ErrorMessage = "Informe apenas M ou F")]

    public string alunoSexo { get; set; }
 

    [Required]

    public int cursoCodigo { get; set; }


    [
ForeignKey("cursoCodigo")]

    public virtual Curso Curso { get; set; }

  }

}

Data Annotations usados na definição da classe Aluno:

A  Annotation  [Table("Aluno")] define o nome da tabela que será gerada;

A Annotation [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
define que alunoCodigo é do tipo identity

A Annotation   [StringLength(50)] define que o campo  string alunoNome terá um tamanho de 50 caracteres;

A Annotation [Required(ErrorMessage="O nome do aluno é obrigatório")] define que o campo alunoNome é obrigatório;

A Annotation  [
Required , StringLength(1, ErrorMessage = "Informe apenas M ou F")] define uma restrição para o campo alunoSexo.

A Annotation [ForeignKey("cursoCodigo")] informa que o atributo será a chave estrangeira da tabela Aluno;

Repita o processo acima e cria a classe Curso definindo nesta classe o código abaixo:

using System.ComponentModel.DataAnnotations;

using System.Collections.Generic;
 

namespace Escola_CRUD_EF41.Model

{

 

  [Table("Curso")]

   public class Curso

  {

   [Key]
   [
DatabaseGenerated(DatabaseGeneratedOption.Identity)]

   public int cursoCodigo { get; set; }


   [
StringLength(50)]
   [
Required(ErrorMessage = "O nome do curso é obrigatório")]

   public string cursoNome { get; set; }
 

   public virtual ICollection<Aluno> Alunos { get; set; }

 }

}

Data Annotations usados na definição da classe Curso:

A  Anootation  [Table("Curso")] define o nome da tabela que será gerada;

A Annotation [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
define que cursoCodigo é do tipo identity

A Annotation   [StringLength(50)] define que o campo  string cursoNome terá um tamanho de 50 caracteres;

A Annotation  [Required(ErrorMessage = "O nome do curso é obrigatório")] define que o campo cursoNome do curso é obrigatório;

Vamos agora entender o que foi feito e  o por quê ?

Definimos as 2 classes do nosso domínio pois o Entity Framework vai usar essa informação para gerar o mapeamento objeto relacional , criar o banco de dados e as tabelas para a nossa aplicação ASP .NET.  Esse recurso chama-se Code-First (o código primeiro) e indica que partimos das definições de nossas classes de domínio para gerar o banco de dados e as tabelas e o contexto com o mapeamento objeto relacional.

Observe que todas as classes utilizam o namespace System.ComponentModel.DataAnnotations. Mas o para que serve isso ?

Os Data annotations são usados para definir atributos e validação nas classes alterando assim a convenção padrão usada pelo Entity Framework 4.1.

Os atributos Data Annotation foram introduzido no .NET 3.5 como uma forma de adicionar a validação para as classes usadas por aplicações ASP.NET. Desde aquela época, o RIA Services começou a usar anotações de dados e eles agora fazem parte do Silverlight também. No EF 4.0 o Code-First permite que você construa um EDM usando código (C#/VB .NET) e também permite realizar a validação com atributos Data Annotations.

Para este recurso devemos usar o namespace System.ComponentModel.DataAnnotations pois é ele que provê atributos de classes (usados para definir metadados) e métodos que podemos usar em nossas classes para alterar as convenções padrão e definir um comportamento personalizado que pode ser usado em vários cenários.  A seguir temos a relação das principais Annotations suportadas pelo Entity Framework 4.1:

Key - Usada para especificar que uma propriedade/coluna é parte da chave primária da entidade e se aplica apenas a propriedades escalares;
StringLength - Usada para especificar o tamanho máximo de uma string;
ConcurrencyCheck - Usada para especificar que uma propriedade/coluna tem um modo de concorrência "fixed " no modelo EDM;
Required : - Usada para especificar que uma propriedade/coluna é não-nula e aplica-se a propriedades escalares, complexas, e de navegação;
ColumnUsada para especificar o nome da coluna, a posição e o tipo de dados ;
Table Usada para especificar o nome da tabela e o esquema onde os objetos da classe serão atualizados;
ForeignKey - Usado em uma propriedade de navegação para especificar a propriedade que representa a chave estrangeira da relação
DatabaseGenerated -Usada em uma propriedade para especificar como o banco de dados gera um valor para a propriedade, ou seja, Identity, Computed ou None;
NotMapped – Usada para definir que a propriedade ou classe não estará no banco de dados;

Então quando o Entity Framework encontrar as nossas definições de atributos e validações ele vai adotar a convenção que definimos e não a padrão que ele seguiria se apenas tivéssemos definidos as classes sem as anotações. Usamos os seguintes atributos na definição de nossas classes:

Mas o que vem a ser essas convenções ???

Consultando o dicionário teremos a seguinte definição:  Acordo, pacto, contrato: convenção verbal , aquilo que está geralmente admitido ou tacitamente contratado.

Em nosso contexto, podemos dizer que uma convenção é uma regra padrão pelo qual não teremos que fazer algumas configurações de mapeamento para nossas entidades, sendo que o EF4 vai , baseado nessas convenções, realizar as tarefas de forma automática.

E quais seriam então essas convenções ???

Segue um resumo :

  1. Se você não definir um endereço para o servidor , o EF4 CTP5 irá tentar realizar uma conexão local;
  2. O nome da tabela criada será o nome da classe no plural (usando a sintaxe da gramática da língua Inglesa.No exemplo: Alunoes e Cursoes);
  3. Se não for definido um nome para o banco de dados , ele será criado com o nome do projeto mais o nome da classe do contexto;
  4. Se uma classe tem uma propriedade de navegação para outra classe e esta possuir uma coleção da primeira, o EF infere que existe um relacionamento do tipo um-para-muitos entre as classes;
  5. Se existe uma propriedade com nome igual ao nome da classe mas termina em Id ('nomeClasse'+Id) ou se o nome da propriedade é apenas Id esta propriedade será a chave primária;
  6. Se a chave primária é do tipo inteiro ele é do tipo Identity;

Na definição da classe Aluno poderíamos usar o atributo CustomValidation que permite definirmos uma classe customizada para realizar a validação. Como exemplo poderíamos ter definido uma classe interna usando o modificador sealed , que não permite que outras classes herdem desta classe.

Abaixo um exemplo onde a classe Validacao , realiza a validação da propriedade alunoNome:

  internal sealed class Validacao

    {

       public static ValidationResult ValidarNome(string alunoNome)

       {

            if (alunoNome.Length > 3)

                return ValidationResult.Success;

            return new ValidationResult("O nome deve ter mais que 3 caracteres !");

       }

    }

Usando o atributo : [CustomValidation]

[Required, CustomValidation(typeof(Validacao), "ValidarNome")]
public string alunoNome { get; set; }

Esse atributo exige que uma classe seja criada usando métodos estáticos que retornam um objeto do tipo ValidationResult.

No construtor do atributo, passamos o tipo da classe (typeof(ClasseCustomizada))
e uma string contendo o nome do método que vai executar a validação.

Além das validações feitas com atributos podemos também usar a API Fluente e através da implementação da interface IValidatebleObject.(Assunto para outro artigo)

Nas classes definidas usamos as propriedades do tipo virtual para indicar que um tipo complexo (Aluno e Curso) possui um relacionamento através de uma chave estrangeira (ForeignKey) de forma que o EF vai usar esta propriedade como propriedade de navegação entre as entidades. Ex: Aluno.Curso.cursoNome .

Definimos também a propriedade Alunos  como do tipo ICollection para indicar um relacionamento um-para-muitos.  Assim para cada Curso existem muitos alunos.

Para podermos acessar o banco de dados vamos precisar definir um contexto.

Definindo o Contexto

Agora vamos definir uma classe que irá usar herdar da API DbContext e referenciar as classes que acabamos de criar

Selecione a pasta Model e no menu Project selecione Add Class e a seguir informe o nome Contexto.cs e clique no botão Add;

A seguir defina o código abaixo neste classe:

using System.Data.Entity;

namespace Escola_CRUD_EF41.Model
{

 public class Contexto : DbContext
 {

   public Contexto()
       :
base("EscolaMacoratti_CodeFirst"
) { }

   public DbSet<Aluno> Alunos { get; set; }
 
 public DbSet<Curso> Cursos { get; set; }
 }
}

Um dos recursos do EF 4.1 são as duas classes :
ObjectContext e ObjectSet;

- ObjectContext permite consultar, controle de alterações e salvar no banco de dados.
- ObjectSet encapsula os conjuntos de objetos semelhantes.

O DbContext é um invólucro para ObjectContext e além disso esta classe contém:

- Um conjunto de APIs que são mais fáceis de usar do que a exposta pelo ObjectContext;
- As APIs que permitem utilizar o recurso do Code-First e as convenções;

O DbSet é um invólucro para ObjectSet.

 

A nossa classe Contexto herda de DbContext e define as propriedades Alunos, Cursos e Professores com as quais temos acesso as tabelas do banco de dados.

O construtor da classe  define o nome do banco de dados que será criado pelo Entity Framework.

Isso é tudo que você precisa para iniciar a persistência e a consulta aos dados. Mas podemos melhorar...

Vamos usar outro recurso do Entity Framework que é o inicializador do banco de dados onde vamos definir a criação e carga dos dados iniciais.

Como já disse o EF irá criar um banco de dados com o nome EscolaMacoratti_CodeFirst que definimos no construtor da classe de contexto: Contexto.

Vamos então criar outra classe para iniciar o banco de dados e nela vamos definir as seguintes regras:

– O EF vai se conectar em uma instância padrão do SQL Express (./SQLEXPRESS) criando o banco de dados EscolaMacoratti_CodeFirst  se ele ainda não existir;
– O nome do banco de dados será o definido na classe de contexto com o construtor 'base' ou então será o nome da classe.(conforme já mostrei no artigo :
Entity Framework 4 - Usando POCO, Code First e as convenções padrão)
- O EF deverá implementar os requisitos que definimos via Data Annotations;

Nota:Lembre-se que até agora ainda não configuramos uma string de conexão no arquivo web.config.

Selecione a pasta Model e no menu Project selecione Add Class e a seguir informe o nome InicializaBD.cs e clique no botão Add;

A seguir defina o código abaixo nesta classe:

using System.Data.Entity;
 

namespace Escola_CRUD_EF41.Model

{

  public class InicializaBD

  {

    public static void geraTabelas(bool excluiBD)

    {

      using (Contexto _contexto = new Contexto())

      {

           if (excluiBD)

             _contexto.Database.Delete();


          
var curso = new Curso { cursoNome = "Quimica" };

          _contexto.Cursos.Add(curso);


          
var aluno = new Aluno { alunoNome = "Jefferson",alunoEmail = "jeff@bol.com.br", alunoSexo = "M", Curso = curso };

          _contexto.Alunos.Add(aluno);
 

          _contexto.SaveChanges();

      }

    }

  }

}

A classe InicializaBD define o método estático geraTabelas(bool excluiBD) que recebe um parâmetro boleano que indica se vamos excluir o banco de dados gerado. Para isso usamos o método Delete da classe DataBase do contexto.

Definimos a seguir instâncias da classe Curso e Aluno atribuindo valores e em seguida persistindo as informação no banco de dados via método SaveChanges().

Falta agora definir onde vamos chamar a classe InicializaBD. Como estamos criando uma aplicação ASP .NET o arquivo Global.asax seria um ótimo local mas vou fazer diferente.

Vou incluir dois WebForms no projeto:

Selecione o projeto e no menu Project -> Add New Item inclua um novo WebForm com o nome Default.aspx. Repita o processo acima e inclua um novo WebForm chamado ExibeDados.aspx.

Abaixo temos o leiaute do WebForm Default.aspx:

A seguir inclua o código abaixo no arquivo code-behind Default.aspx.cs:

using Escola_CRUD_EF41.Model;
namespace Escola_CRUD_EF41
{
    public partial class Default : System.Web.UI.Page
    {
        protected void btnGeraBD_Click(object sender, EventArgs e)
        {
            if (IsPostBack)
            {
                try
                {
                    InicializaBD.geraTabelas(true);
                    lblmensagem.Text = "Banco de dados e tabelas criados.";
                }
                catch (Exception ex)
                {
                    lblmensagem.Text = " Erro : " + ex.Message;
                }
            }
        }
        protected void btnExibeDadaos_Click(object sender, EventArgs e)
        {
            Response.Redirect("~/ExibeDados.aspx");
        }
    }
}
 

O código do evento Click do botão - Criar Banco de dados e tabelas -  chama o método geraTabelas(true) passando o parâmetro true e com isso indicando que desejamos excluir o banco de dados existente;

O evento Click do botão - Exibir Dados Existentes - irá chamar o formulário web ExibeDados.aspx.

A seguir temos o leiaute do WebForm ExibeDados.aspx:

A seguir inclua o código abaixo no arquivo code-behind ExibeDados.aspx.cs :

using System;

using System.Linq;

using Escola_CRUD_EF41.Model;
 

namespace Escola_CRUD_EF41

{

  public partial class ExibeDados : System.Web.UI.Page

  {

    protected void Page_Load(object sender, EventArgs e)

    {

       using (var contexto = new Contexto())

       {

         GridView1.DataSource = contexto.Cursos.ToList();

         GridView1.DataBind();

       }

     }

  }

}

O código ao lado colocado no evento Load do formulário irá criar um a instância do Contexto e obter a lista de Cursos exibindo no controle GridView.
Defina a página Default.aspx como sendo a página inicial do projeto (Set as Start Page) e execute o projeto:
Você verá a página Default.aspx exibindo os dois botões. Clique no botão para criar o banco de dados, e, após alguns segundos, você verá uma mensagem de sucesso:
Após isso clique no botão para exibir dados e a página ExibeDados.aspx será chamada exibindo os cursos gravados na tabela Curso:
Agora vamos verificar se o Entity Framework foi obediente e seguiu as nossas anotações.

Vamos verificar se isso é realmente verdade abrindo o SQL Server Management Studio e espiando o nosso SQL Server .

Abaixo vemos o banco de dados e as tabelas aluno o curso criadas :

Observe em cada tabela o campo chave primária, os nomes dos campos e os campos chave estrangeira.

Confira e você vai ver que ele fez tudo direitinho seguindo nossas instruções usando o Data Annotations.

Selecionando o banco de dados e realizando uma consulta SELECT nas tabelas iremos constatar que os registros foram persistidos nas tabelas:

Tabela Aluno
Tabela Curso

E como prometemos observando o código não vimos nenhuma instrução SQL.

Em resumo as tarefas realizadas pelo EF 4.1 foram:

Se você chegou até aqui e constatou como é simples trabalhar com acesso a dados usando o Entity Framework pode se interessar pelo próximo artigo onde irei mostrar do como criar uma aplicação ASP .NET com acesso a dados usando os recursos do Ef 4.1.

Na segunda parte do artigo vamos realizar as operações CRUD usando ASP .NET e o Entity Framework 4.1 - Entity Framework 4.1 - ASP .NET com Code-First (C#) - II

Pegue o projeto completo aqui : Escola_CRUD_EF41.zip

"Portanto agora nenhuma condenação há para os que estão em Cristo Jesus, que não andam segundo a carne, mas segundo o espírito." Romanos 8:1

Referências:


José Carlos Macoratti