O Entity Framework Code First provê suporte ao mapeamento de heranças. Podemos fazer este mapeamento de duas maneiras, via Table Per Hierarchy ou Table Per Type. Vamos ver seu funcionamento e suas diferenças neste post.
Para os exemplos desta publicação utilizaremos a mesma estrutura de classes para os dois modelos de mapeamento. A estrutura proposta engloba três classes: Veiculo, Carro e Aviao. Nessa estrutura as classes Carro e Aviao herdam de Veiculo. O diagrama deste modelo pode ser visto na figura abaixo:
O código necessário para criação destas classes é bastante simples e pode ser conferido logo abaixo:
Podemos notar que as classes Carro e Aviao herdam de Veiculo e adicionam algumas propriedades próprias, que fazem parte do seu domínio.
Após criadas as entidades vamos criar nosso DataContext, objeto que funcionará como ponte entre a nossa aplicação e o nosso banco de dados. Para criação do DataContext utilizaremos o bloco abaixo, ele possui todos os recursos necessários para começarmos a trabalhar com o mapeamento de heranças do Entity Framework Code First.
Podemos ver que este DataContext possui algumas configurações interessantes, sendo elas:
– Utilização do inicializador DropCreateDataBaseAlways: como estamos demonstrando um exemplo que irá afetar a estrutura do banco de dados, vamos utilizar um inicializador que irá apagar e reconstruir o banco de dados toda vez que executarmos a aplicação. Este inicializador é favorável quando estamos fazendo testes desse tipo, para ambientes de produção seu uso não é indicado.
– Remoção da convenção PluralizingTableNameConvention: para evitarmos a criação de tabelas com nomenclaturas do EF Code First.
E para realizarmos os testes em nossa base de dados utilizaremos um bloco de código que fará inclusões na tabela Veiculos, e então observaremos o comportamento do Entity Framework Code First ao mapear as entidades e criar o banco de dados. O bloco que utilizaremos para fazer as inclusões é o seguinte:
Pronto, agora falaremos mais específicamente sobre o mapeamento do Entity Framework Code First…
Table Per Hierarchy
Table Per Hierarchy (TPH) descreve o mapeamento de heranças para uma única tabela que utiliza uma coluna para descriminar um subtipo de outro. Quando criamos uma herança no modelo por padrão será utilizada esta convenção. Isto é, toda a estrutura de heranças será mapeada para uma única tabela, onde será criado um campo para diferenciar um tipo do outro. Se executarmos nosso bloco de código de teste nossa base de dados será criada e ficará deste modo:
Note que todas as propriedades de todas as classes pertencentes a esta hierárquia foram criadas na mesma tabela e que uma coluna chamada “Discriminator” foi criada. Essa coluna é utilizada para diferenciar um tipo de dados do outro. Se observarmos os dados que foram salvos na base de dados iremos entender essa diferença, veja a figura abaixo:
Agora podemos ver que o mapeamento Table Per Hierarchy criou uma única tabela e que utiliza uma coluna para diferenciar os dados de um tipo dos dados do outro tipo de dados.
Table Per Type
Table Per Type (TPT), como o próprio nome já diz, irá criar uma tabela para cada tipo de dados, com uma Foreign Key fazendo relacionamento com a tabela mapeada para a classe base. Para utilizarmos este mapeamento devemos fazer uma modificação na nossa classe DataContext, registrando o mapeamento do modelo como TPT. A classe DataContext deve ficar como:
Veja que a única modificação que fizémos foi explicitamente mapear as classes Carro e Aviao para suas respectivas tabelas. Depois de executarmos a aplicação, veremos que cada classe agora possui sua respectiva tabela, como na figura abaixo:
E se olharmos os dados no banco de dados, eles estão assim:
Veja que os dados das tabelas filhas estão ligados aos dados da tabela pai através da coluna Id. Este é o elo de ligação entre as entidades na estrutura de herança.
Por
Fernando Henrique Inocêncio Borba Ferreira
Microsoft Most Valuable Professional – Data Platform Development
Seu artigo facilitou muito a minha vida! Parabéns pelo artigo e obrigado!
Olá Maicon!
Fico feliz de poder ajudar!
Obrigado pelo comentário!
[]s!
Ajudou muito mesmo. Obrigado.
Caraca…eu já estava desistindo de utilizar o Entity, mas agora vou me aprofundar mais nesse ORM.
Bacana, bem esclarecedor. Parabéns!
[…] performace diferente entre os 3 tipos diferentes. Também queria falar dos três tipos, uma vez que meu mestre falou somente de 2 ano […]
Prezado Fernando
Parabéns pelo post, claro, simples e objetivo.
Obrigado, Luis! Realmente, muito obrigado. []s!
[…] diferente entre os três tipos diferentes. Também queria falar dos três tipos, uma vez que meu mestre falou somente de dois ano […]
Excelente post!
Seu blog virou uma referencia para mim!
Muito obrigado
Muito bom o artigo! Obrigado hermano!
Qual das duas obteve maior performance? 🙂
Olá Rodrigo,
A questão do “ganho ou perda” de performance neste caso está mais próximo a estrutura de seu banco de dados.
Do lado do .NET Framework isso é transparente, mas para o banco de dados, o número de colunas, complexidade das chaves, tipos de indíces e o particionamento de tabelas podem pesar de maneira diferente.
Acredito que, vale a pena conversar com o DBA de seu projeto, e/ou fazer testes com as diferentes abordagens.
[]s!
Fernando, e para trazer uma lista de Veículos trazendo os dados das tabelas filhas como faço? geralmente eu faço um .Include, porém no exemplo não existe um DbSet para Carro e Avião, eu poderia criar um para cada entidade filha?
Vamos lá, veja se pode me ajudar… Eu tenho os objestos Fornecedor, Vendedor, Cliente e outros que Herdam de Pessoa. Seria possível utilizar uma tabela só para todos, ou seja, opção condicional no mapeamento para essa mesma tabela e com algum campo que os diferencie. exemplo Tipo?