Entity Framework -  Definindo um modelo com herança Table Per Type


Este série de artigos é dedicada ao Entity Framework e tem o objetivo de dar uma introdução básica e uma visão geral de como usar este importante recurso da plataforma .NET. Os artigos são parcialmente baseados na documentação da MSDN de onde são citadas as respectivas referências.

Iniciando com o Entity Framework

Conceito

A ADO .NET Entity Framework foi projetado para permitir que a criação de aplicações com acesso a dados use o modelo de programação feito contra um modelo conceitual ao invés do antigo modelo de programação feito diretamente contra um banco de dados relacional. O objetivo é diminuir a quantidade de código e o tempo de manutenção necessária exigida nestas aplicações orientada a dados.

A ADO .NET Entity Framework é uma framework que abstrai o esquema de um banco de dados relacional e o apresenta como um modelo conceitual. Abaixo a figura que representa a arquitetura em camadas do Entity Framework: (Até o lançamento do VS 2010, previsto para o primeiro trimestre.)

Data Source :  Representa a base da arquitetura onde estão armazenados os dados;

Data Providers :  Os dados são acessados por um ADO.NET data provider.  Até o momento tanto o SQL Server como o MySQL são suportados e provavelmente em breve os principais RDBMS do mercado (Oracle, MySQL, DB2, Firebird, Sybase, VistaDB, SQLite, ...) terão um provider para o EF.

Entity Data Model (EDM) :  O  Entity Data Model  é constituído de 3 partes:

  • Conceptual schema definition language (CSDL) : Declara e define entidades, associações, herança, etc,. As Entity classes são geradas a partir deste esquema;
  • Store schema definition language (SSDL) : Metadados que descreve o container de armazenamento (=banco de dados) que persiste os dados;
  • Mapping specification language (MSL) : Mapeia as entidades no arquivo CSDL para tabelas descritas no arquivo SSDL;

Entity Client : O EntityClient é um provedor gerenciado ADO.NET que suporta o acesso a dados descritos no EDM. Ele é similar ao SQLClient, e fornece diversos componentes como EntityCommand, EntityConnection e EntityTransaction;

Object Services : Este componente permite realizar consultas, atualizações, inclusões e exclusões nos dados, expressados como objetos CLR fortemente tipados que são instâncias de entity types. Ele da suporte tanto a consultas Entity SQL como LINQ to Entities;

Entity SQL (ESQL) : É uma derivação da Transact-SQL, projetada para consultar e manipular entidades definidas no EDM. Ele dá suporte herança e associação sendo que tanto os componentes Object Services como os componentes Entity Client podem executar instruções Entity SQL;

LINQ to Entities : É uma linguagem de consulta fortemente tipada para consultar entidades definidas no EDM;
 

Criar, modificar e deletar objetos e aplicar estas alterações ao banco de dados é muito fácil com o Entity Framework. Através do Object Services que gerencia todas as alterações feitas nos objetos e gera e executa as instruções T-SQL que irão realizar as operações de inclusão, alteração  e exclusão contra a fonte de dados. Tudo isso é feito através da chamada do método SaveChanges do ObjectContext (equivalente ao SubmitChanges do LINQ to SQL).

Quando usamos uma ferramenta OR/M como o Entity Framework desejamos definir da melhor forma possível o nosso modelo de entidades de forma a que tenhamos um bom desempenho e que o esforço necessário para tratar o modelo seja o mínimo possível. Como todos já sabem o modelo relacional de banco de dados não se ajusta adequadamente ao paradigma da orientação a objetos e uma ferramenta OR/M que se preze deve oferecer recursos para tornar a discrepância entre esses dois mundos o menos traumática possível. 

Na programação orientada a objetos quando um objeto é um tipo de outro objeto podemos usar a herança para compartilhar suas propriedades de forma que as propriedades da classe base são expostas diretamente para o tipo derivado.

A herança é um recurso da orientação objeto que não existe nativamente no mundo relacional e uma ferramenta OR/M deve oferecer recursos para que possamos implementar a herança em nosso modelo de entidades de forma a obter um melhor rendimento. Felizmente o Entity Framework oferece esses recursos.

Nota:  No paradigma da orientação a objetos a herança é muitas vezes usada para representar que apresentam uma relacionamento do tipo É-um. Ou seja quando um objeto é uma especificação do outro objeto.

O Entity Framework suporta 3 modelos diferentes de herança:

  1. Table Per Type
  2. Table Per Hierarchy (Single Table Inheritance)
  3. Table Per Concrete Class

O modelo Table-per-Type é uma forma de modelar a herança onde cada entidade é mapeada para uma tabela diferente na camada de armazenamento. Cada tabela contém todas as propriedades para a entidade bem como uma chave que mapeia em última instância a tabela raiz/entidade.

A herança do tipo Table per Type (TPT) define uma herança que é descrita no banco de dados com tabelas separadas onde uma tabela fornece detalhes adicionais que descrevem um novo tipo baseado em outra tabela. Este cenário é mais comum do que se imagina e pode ser visto na figura abaixo que exibe duas tabelas onde podemos aplicar a herança TPT.

Vejamos um modelo bem simples onde podemos usar a herança TPT:

A figura ao lado mostra o relacionamento entre a tabela Contato e Clientes.  Onde um contato pode ter uma entidade cliente relacionada.

 

Vamos mostrar um exemplo prático definindo um modelo conceitual do tipo Table Per Type (TPT) para isso eu vou criar um modelo de dados bem simples usando o Microsoft SQL Server 2008 Express e o SQL Server 2008 Management Studio Express Edition.

Abaixo temos as tabelas : Pessoa, Supervisor, Professor , Aluno e Estagiário.

 Nosso objetivo é implementar o modelo conceitual da herança Table Per Type suportada no EDM de forma que possamos ter acesso as informações compartilhadas entre as tabelas.

Eu não vou entrar no mérito do modelo de dados pois ele esta sendo usado apenas para ilustrar a implementação da herança TPT no Entity Framework.

NotaPegue o script para gerar as tabelas aqui : Escola.sql

Neste caso estamos modelando uma hierarquia de herança onde temos a entidade base Pessoa e 3 entidades que herdam de Pessoa :: Aluno, Supervisor e Professor.  Além disso temos a entidade Estagiario que herda da entidade Aluno.

Implementando o modelo da herança Table Per Type no Entity Framework

Vamos usar o Visual Studio 2008 e criar um projeto do tipo Windows Forms Application com o nome EF_herancaTPT;

A seguir no menu Project selecione Add New Item e escolha o template ADO .NET Entity Data Model, informe o nome Escola.edmx e clique Add;

A seguir selecione a opção Generate From dataBase e clique no botão Next>;

A seguir selecione a conexão com o banco de dados Escola e clique em OK;

A seguir aceite os valores padrões informados para a connection String e clique no botão Next>;

Na janela seguinte selecione as tabelas criadas , aceite o nome padrão para o namespace e clique em Finish;

Como resultado final será exibida o modelo de entidades criados no descritor do Entity Framework. O nosso EDM:

Pois é agora que o nosso trabalho vai começar.

Vamos começar alterando os nomes das entidades alterando o nome da propriedade Entity Set Name para o plural em todas as entidades. Veja abaixo a janela de propriedades para a entidade Pessoa:

 Da mesma forma altere a propriedade Entrity Set Name  das demais entidades para:
  • Professores
  • Alunos
  • Estagiarios
  • Supervisores

A seguir vamos excluir todas as associações geradas no EDM selecionando cada uma delas e pressionando a tecla Delete. Na figura abaixo vemos as associações selecionadas e em seguida excluídas:

A seguir selecione a entidade Pessoa e clique com o botão direito do mouse selecionando Add -> Inheritance;

Será apresentada a janela Add Inheritance onde iremos selecionar a entidade base - Pessoa e a derivada Aluno clicando em OK;

Repita o procedimento acima incluindo a herança para as entidades derivadas Professor e Supervisor;

Finalmente selecione a entidade Aluno e clique com o botão direito e selecione Add -> Inheritance e na janela Add Inheritance selecione a entidade base Aluno e a entidade derivada Estagiario;

A final deveremos ter o seguinte modelo no descritor do EF:

A seguir vamos excluir as propriedades AlunoID, estagiarioID, supervisorID e professorID das entidades pois será herdada a propriedade pessoaID;

Para excluir essas propriedades selecione-as, clique com o botão direito do mouse, e selecione a opção Delete;

Como excluímos essas propriedades das entidades derivadas, a coluna pessoaID da tabela correspondente não esta mais mapeada. Precisamos então refazer o mapeamento para que a propriedade pessoaID seja herdada. Vamos lá...

Selecione cada uma das entidades derivadas e na janela  Mapping Details mapeie cada coluna para a coluna pessoaID conforme a figura abaixo mostra para a entidade Supervisor:

Faça este processo para cada entidade derivada incluindo entidade Estagiario.

Pronto. Com isso terminamos de criar o nosso modelo conceitual baseado na herança TPT.

Vamos testar...

Vamos definir no formulário form1.vb, criado por padrão, uma interface bem simples apenas para mostrar que temos acesso as propriedades herdadas pelas entidades derivadas da entidade base.

 Nota:  Eu poderia ter criado uma aplicação console, seria mais simples ainda mas preferi criar uma aplicação Windows Aplication.

No formulário form1.vb inclua os seguintes controles : 5 Labels, 5 ListBox e 5 Button conforme o leiaute da figura abaixo:

Vamos iniciar exibindo a entidade Pessoa e para isso vamos incluir o código abaixo no evento Click do botão Exibir Pessoas:

 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Using escola As New EscolaEntities1
            Dim pessoa = From p In escola.Pessoa _
                                Select p
            For Each p As Pessoa In pessoa
                   lstPessoa.Items.Add(p.nome & "-" & p.email)
            Next
        End Using
    End Sub

Neste código definimos a variável escola como uma instância do nosso Contexto (ObjectContext) e em seguida efetuamos uma consulta retornando os objetos da entidade Pessoa.

Para exibir as propriedades da entidade percorremos a entidade usando um laço For/Each.

Vamos agora exibir a entidade Professor. Para acessar as propriedades herdadas das entidades derivadas incluímos o código a seguir no evento Click do botão : Exibir Professores:

 Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Using escola As New EscolaEntities1
            Dim pessoa = From p In escola.Pessoa.OfType(Of Professor)() _
                                Select p
            For Each p In pessoa
                lstProfessor.Items.Add(p.nome & "-" & p.email & "=>" & p.salario)
            Next
        End Using
    End Sub

 

Neste código usamos o recurso da herança criada no modelo Table Per Type onde selecionamos as propriedades do tipo derivado Professor:

Dim pessoa = From p In escola.Pessoa.OfType(Of Professor)() _
                      Select p

Observe que após feito este procedimento temos acesso as propriedades herdadas : nome e email e da propriedade salario da entidade Professor.

         For Each p In pessoa
                lstProfessor.Items.Add(p.nome & "-" & p.email & "=>" & p.salario)
         Next

Para exibir as propriedades para as demais entidades o procedimento é o mesmo mudando apenas o nome do tipo e da propriedade que desejamos acessar. Veja abaixo o código referente aos demais botões (todos no evento Click):

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Using escola As New EscolaEntities1
            Dim pessoa = From p In escola.Pessoa.OfType(Of Supervisor)() _
                                Select p

            For Each p In pessoa
                lstSupervisor.Items.Add(p.nome & "-" & p.email & "=>" & p.turno)
            Next
        End Using
    End Sub

 

Entidade Supervisor
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
        Using escola As New EscolaEntities1
            Dim pessoa = From p In escola.Pessoa.OfType(Of Aluno)() _
                                Select p

            For Each p In pessoa
                lstAluno.Items.Add(p.nome & "-" & p.email & "=>" & p.nota & "," & p.curso)
            Next
        End Using
    End Sub

 

Entidade Aluno
   
Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
        Using escola As New EscolaEntities1
            Dim pessoa = From p In escola.Pessoa.OfType(Of Estagiario)() _
                                Select p
            For Each p In pessoa
                lstEstagiario.Items.Add(p.nome & "-" & p.email & "=>" & p.curso & "," & p.creditos)
            Next
        End Using
    End Sub

 

Entidade Estagiario

O resultado obtido pode ser visto na figura abaixo :

Uma outra forma de obter o mesmo resultado seria usar o código exibido a seguir que coloquei no evento Click do  botão - Exibir Todos :

 Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click
        Using escola As New EscolaEntities1
            Dim pessoa = From p In escola.Pessoa _
                         Select p
            For Each p As Pessoa In pessoa
                lstPessoa.Items.Add(p.nome & "-" & p.email)
            Next
            For Each tp In pessoa
                If TypeOf tp Is Professor Then
                    Dim p As Professor = DirectCast(tp, Professor)
                    lstProfessor.Items.Add(tp.nome & "-" & tp.email & "=>" & p.salario)
                End If
                If TypeOf tp Is Supervisor Then
                    Dim s As Supervisor = DirectCast(tp, Supervisor)
                    lstSupervisor.Items.Add(tp.nome & "-" & tp.email & "=>" & s.turno)
                End If
                If TypeOf tp Is Aluno Then
                    Dim a As Aluno = DirectCast(tp, Aluno)
                    lstAluno.Items.Add(tp.nome & "-" & tp.email & "=>" & a.nota & "," & a.curso)
                End If
                If TypeOf tp Is Estagiario Then
                    Dim est As Estagiario = DirectCast(tp, Estagiario)
                    lstEstagiario.Items.Add(tp.nome & "-" & tp.email & "=>" & est.creditos)
                End If

            Next
        End Using
    End Sub

Neste código verificamos o tipo de cada entidade e após realizar o casting para o tipo desejado exibimos as propriedades herdades e as específicas:

Nota : Para saber mais sobre Casting no VB .NET veja o meu artigo: VB.NET - Casting

Pegue o projeto completo aqui: EF_HerancaTPT.zip

Dessa forma mostramos um exemplo básico de como implementar a herança TPT no Entity Framework .

Aguarde em breve novos artigos sobre este assunto...

Eu sei é apenas Entity Framework mas eu gosto...

Referências:


José Carlos Macoratti