Entity Frameweork 4 - Usando POCO, Code First e as convenções padrão


Esta prevista para o primeiro semestre deste ano uma nova versão do Entity Framework 4 com muitos ajustes com o objetivo de tornar a ferramenta cada vez melhor.

Enquanto a versão final não sai, vamos falar um pouco sobre uma das suas últimas atualizações, a Entity Framework Feature Community Technology Preview - CTP5, mais precisamente da funcionalidade Code-First e POCO.

Obs: Baixe a versão CTP5 aqui : http://tinyurl.com/5rg52tg

Nota: A versão anterior, a CTP4, já permitia usar o recurso do First-Code com um banco de dados existente mas exigia mais intervenção do desenvolvedor com a CTP5 tudo ficou mais simples.

A CTP5 da Entity Framewor 4 inclui atualizações para o recurso Code First e uma interface simplificada da API (DbContext)

O Entity Framework é uma ferramenta OR/M que realiza o mapeamento objeto relacional gerando entidades e mapeando-as para as tabelas do banco de dados.

Na primeira versão do Entity Framework praticamente não havia o que é conhecido como Code-First (Código Primeiro), ou seja, a possibilidade de gerar o modelo de negócios e suas entidades sem ter que primeiro criar o banco de dados.

Para quem trabalha usando o paradigma da orientação a objetos deveria ser natural iniciar o desenvolvimento pelo modelo de entidades, onde as classes são definidas para representar o domínio do negócio. Posteriormente, e a partir destas classes, seria gerado o banco de dados usado pela aplicação para realizar a persistência das informações.

Deveria ser assim, mas a primeira versão do EF não permitia esse recurso.

Tentando contornar este problema eram feitos certos malabarismos, mas ainda assim, havia uma dependência muito forte do mapeamento gerado pelo EF4 através do Entity Data Model, o que incomodava muita gente; até que a Microsoft liberou atualizações que permitem atualmente uma maior independência do Framework através da criação de classes POCO (Plain Old CLR Objects) que não herdam das classes base geradas pelo EF.

Quando decidimos usar o Code-First não precisamos começar nossa aplicação criando o banco de dados ou definindo um esquema mas podemos iniciar escrevendo classes .NET para definir o modelo de objetos do nosso domínio sem ter que misturar a lógica de persistência de dados com as classes.

O Entity Framework por padrão adota algumas convenções (Conventions) que ele usa para realizar algumas operações.

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.

Na CTP5 , as convenções estão definidas por uma hierarquia de subclasses que implementam um contrato comum chamado IConvention. Uma simples definição deste contrato pode ser visto no código abaixo:


public interface IConvention
{
}

Public Interface IConvention
End Interface

C# VB .NET

Debaixo de IConvention temos elementos como AttributeConfigurationConvention, IDbConvention e IEdmConvention que nos interessam mais , uma vez que eles representam os contratos através dos quais foram implementadas as diversas convenções.

Neste artigo, antes de entrar em detalhes sobre as Convenções, eu vou criar um exemplo no EF4 para mostrar como ele se comporta usando as convenções padrão, para entendermos o seu comportamento e assim alterá-lo se assim desejarmos.

CTP5 - Usando POCO com as convenções padrão

Se usarmos o EF para realizar o mapeamento criando o Entity Data Model teremos uma grande ajuda da ferramenta mas em muitos casos isso ao invés de ajudar acaba atrapalhando pois o código verboso gerado pode ser um problema para integrar com o nosso modelo ou mesmo transmitir usando Web Services.

Vamos então mostrar como usar POCO sem criar o Entity Data Model e também usar o Code-First gerando o banco de dados e as tabelas a partir de nossas entidades.

Mas o que são essas classes POCO ?

POCO - Plain Old CLR Object - são classes simples de domínio que possuem apenas get/sets e um construtor e que não dependem de nenhum framework;
as classes POCO não são obrigadas a herdar de nenhuma outra classe ou implementar nenhuma interface.Portanto as classes POCO  são independente de frameworks.

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 diagram de classes a seguir:

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

O modelo de classes representa um Curso que possui 1 ou mais alunos.

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 cardinalidade é um conceito importante para ajudar a definir o relacionamento, ela define o número de ocorrências em um relacionamento. Para determinar a cardinalidade, deve-se fazer a pergunta relativa ao relacionamento em ambas as direções. No exemplo a seguir, temos

Para implementar o nosso exemplo eu vou usar o Visual Basic 2010 Express Edition e criando um novo projeto chamado EF4_Poco_Escola via menu File->New Project escolhendo o template Windows Forms Application;

Obs: Você vai precisar ter instalado o SQL Server 2005 ou 2008 Express Edition.

No menu Project clique em Add Reference;

Selecione a guia Browse e localize a dll EntityFramework na pasta onde foi instalada a CTP5 (c:\Arquivos Programas)

Os recursos da EF Code First CTP5 tem um nome de assembly atualizado e novo namespace .NET:

No formulário form1.vb vamos definir uma interface bem simples apenas para exibir as mensagens durante a execução do código.

Inclua um controle ListBox (lstResultado) e um botão de comando (btnExecutar) conforme o leiaute da figura abaixo:

Definindo as classes de negócio

Vamos definir as classes Curso e Aluno no projeto da seguinte forma:

1 - No menu Project selecione Add Class e informe o nome Modelo.vb e clique no botão Add;

A seguir defina as classes Curso e Aluno no arquivo Modelo.vb conforme o código abaixo:

Public Class Curso

    Private m_CursoId As Integer
    Public Property CursoId() As Integer
        Get
            Return m_CursoId
        End Get
        Set(ByVal value As Integer)
            m_CursoId = valu
        End Set
    End Property

    Private m_nome As String
    Public Property Nome() As String
        Get
            Return m_nome
        End Get
        Set(ByVal value As String)
            m_nome = value
        End Set
    End Property

    Private m_escolaid As Integer
    Public Property EscolaId() As Integer
        Get
            Return m_escolaid
        End Get
        Set(ByVal value As Integer)
            m_escolaid = value
        End Set
    End Property

    Public Property Alunos() As ICollection(Of Aluno)
        Get
            Return m_Alunos
        End Get
        Set(ByVal value As ICollection(Of Aluno))
            m_Alunos = value
        End Set
    End Property
    Private m_Alunos As ICollection(Of Aluno)
End Class

Public Class Aluno

    Private m_AlunoId As Integer
    Public Property AlunoId() As Integer
        Get
            Return m_AlunoId
        End Get
        Set(ByVal value As Integer)
            m_AlunoId = value
        End Set
    End Property

    Private m_nome As String
    Public Property Nome() As String
        Get
            Return m_nome
        End Get
        Set(ByVal value As String)
            m_nome = value
        End Set
    End Property

    Private m_Email As String
    Public Property Email() As String
        Get
            Return m_Email
        End Get
        Set(ByVal value As String)
            m_Email = value
        End Set
    End Property

    Public Property Curso() As Curso
        Get
            Return m_curso
        End Get
        Set(ByVal value As Curso)
            m_curso = value
        End Set
    End Property
    Private m_curso As Curso

End Class

2 - No menu Project selecione Add Class e informe o nome AcessoDados.vb e clique no botão Add;

Vamos definir a classe AcessoDados que herda de DbContext que permite o acesso mais fácil a consultas e  trabalhar com os dados das entidades como objetos.

Imports System.Data.Entity

Public Class AcessoDados

    Public Class ContextoCurso
        Inherits DbContext

        Private m_Cursos As DbSet(Of Curso)
        Public Property Cursos() As DbSet(Of Curso)
            Get
                Return m_Cursos
            End Get
            Set(ByVal value As DbSet(Of Curso))
                m_Cursos = value
            End Set
        End Property

        Private m_Alunos As DbSet(Of Aluno)
        Public Property Alunos() As DbSet(Of Aluno)
            Get
                Return m_Alunos
            End Get
            Set(ByVal value As DbSet(Of Aluno))
                m_Alunos = value
            End Set
        End Property
    End Class

End Class
Um dos aprimoramentos digno de nota que o CTP5 é uma versão simples das 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.

Definindo o código para executar a criação das tabelas do banco de dados e a persistência dos dados

Vamos agora definir o código no formulário form1.vb no evento Click do botão de comando vamos chamar a rotina geraTabelasBD:

Private Sub btnExecutar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExecutar.Click
   geraTabelasBD()

End Sub

O código da rotina geraTabelasBD é visto a seguir:

    Private Sub geraTabelaBD()

        lstResultado.Items.Clear()
        lstResultado.Items.Add("Gerando as tabelas e o banco de dados")
        Using contextoCurso As New ContextoCurso()

            Dim curso As New Curso() With {
              .Nome = "Entity Framework",
              .EscolaId = 100
            }

            lstResultado.Items.Add("")
            lstResultado.Items.Add("Entidade Curso criada com sucesso: nome='Entity Framework', Escolaid=100")

            contextoCurso.Cursos.Add(curso)

            lstResultado.Items.Add("")
            lstResultado.Items.Add("O curso foi incluído na coleção Cursos")

            Dim macoratti As New Aluno() With
            {
                 .Nome = "Jose Carlos Macoratti",
                 .Curso = curso,
                 .Email = "macoratti@yahoo.com"
            }

            lstResultado.Items.Add("")
            lstResultado.Items.Add("Entidade Aluno criada com sucesso: nome='Jose Carlos Macoratti'")

            contextoCurso.Alunos.Add(macoratti)

            lstResultado.Items.Add("O aluno foi incluído na coleção Alunos")

            Dim registros As Integer = contextoCurso.SaveChanges()

            lstResultado.Items.Add("As entidades foram persistidas")
            lstResultado.Items.Add("")
            lstResultado.Items.Add("Registros afetados :" + registros.ToString())

        End Using
    End Sub

Executando o projeto e clicando no botão iremos obter o seguinte resultado:

Feito isso , vamos verificar como o Entity Framework se comportou e quais as operações que ele realizou...

Vamos dar uma olhada no SQL Server instalado na máquina local através do SQL Server Management Studio...

Na figura abaixo vemos que o banco de dados EF4_Poco_Escola.AcessoDados+ContextoCurso foi criado.

Foram criadas também as tabelas Alunos e Cursos e também foram persistidas os dados conforme mostra a figura a seguir:

Obs: O nome das tabelas geradas foram Alunoes e Cursoes. O EF4 aplicou uma convenção baseada na gramática inglesa que cria a tabela com o nome da classe no plural.(*)

O conteúdo as tabelas Cursos e Alunos exibindo os dados incluídos:

Estrutura das tabelas Cursos e Alunos criadas:

Partindo das nossas classes POCO o EF4 - CTP5 realizou todo o trabalho de criação do banco de dados e das tabelas e também da persistência das informações.

Resumindo o EF4 fez o seguinte:

Caramba !!! Como ele sabe fazer tudo isso sozinho ????

Elementar meu caro, ele usa as convenções pré-definidas.

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;

Você pode alterar as convenções padrão definindo suas próprias convenções e assim ter um maior controle sobre as tarefas realizadas mas isso é um assunto para outro artigo.

Pegue o projeto completo aqui: EF4_Poco_Escola.zip (versão VB .NET) e EF4_Poco_Conventions.zip (Versão C#)

Eu sei é apenas EF, mas eu gosto...

Referências:

José Carlos Macoratti