Neste artigo veremos como definir relacionamentos usando o EF Core de forma simples e direta. |
Quando falamos em relacionamentos usando o EF Core isso significa em como a
associação entre as entidades vai se refletir no relacionamento entre as tabelas
no banco de dados.
Os principais tipos de relacionamentos usados no EF Core são:
One-to-One - Um-para-Um
One-to-Many - Um-para-Muitos
Many-to-Many - Muitos-para-Muitos
Os termos usados para definir os relacionamentos são:
- Entidade dependente: É a entidade que contém as propriedades da chave estrangeira. A entidade filha.
- Entidade principal: É a entidade que contém as propriedades da chave primária/alternativa. A entidade 'pai' do relacionamento.
- Chave principal: Propriedade usada para identificar exclusivamente a entidade principal. Esta pode ser a chave primária ou uma chave alternativa.
- Chave estrangeira: Propriedade na entidade dependente que é usada para armazenar os valores da chave principal para a entidade relacionada.
- Propriedade de navegação: uma propriedade definida na entidade principal e/ou dependente que faz referência à entidade relacionada.
- Propriedade de navegação da coleção: uma propriedade de navegação que contém referências a muitas entidades relacionadas.
- Propriedade de navegação de referência: uma propriedade de navegação que contém uma referência a uma única entidade relacionada;
- Propriedade de navegação inversa: Ao discutir uma propriedade de navegação específica, este termo se refere à propriedade de navegação na outra extremidade do relacionamento.
O padrão mais comum para relacionamentos é ter propriedades de navegação definidas em ambas as extremidades do relacionamento e uma propriedade de chave estrangeira definida na classe de entidade dependente.
Se um par de propriedades de navegação for encontrado entre dois tipos, eles serão configurados como propriedades de navegação inversas do mesmo relacionamento;
Se a entidade dependente contiver uma propriedade com um nome que corresponda a um desses padrões, ela será configurada como a chave estrangeira;
1- Relacionamento One-to-One
Para configurar um relacionamento um-para-um, os desenvolvedores podem usar a abordagem da Convenção ou podem até mesmo usar a abordagem da API Fluent.
Em teremos de tabelas de banco de dados um relacionamento Um-para-Um ocorre quando há apenas uma linha em uma tabela que está vinculada a apenas uma linha em outra tabela e vice-versa.
Os relacionamentos um para um têm uma propriedade de navegação de referência em ambos os lados. Eles seguem as mesmas convenções dos relacionamentos um-para-muitos, mas um índice exclusivo é introduzido na propriedade de chave estrangeira para garantir que apenas um dependente esteja relacionado a cada principal.
A propriedade de Navegação em ambas as classes retorna o objeto, e, isso torna difícil para a O EF Core determinar qual classe depende de qual. Portanto, configurar o relacionamento um para um usando a convenção padrão pode não ser tão simples como parece.
Para isso definimos na entidade pendente (Endereco)
a propriedade de Navegação AlunoId que vai atuar
como chave estrangeira.
Exemplo: Aluno e Endereco, onde um aluno pode ter apenas um endereço.
1- Aluno
public class Aluno
{
public int Id { get; set; }
public string? Nome { get; set; }
public Endereco? Endereco { get; set; }
}
|
2- Endereco
public class Endereco
{
public int Id { get; set; }
public string? Rua { get; set; }
public string? Cidade { get; set; }
public string? Estado { get; set; }
public int AlunoId { get; set; }
public Aluno? Aluno { get; set; }
}
|
2- Relacionamento One-to-Many
Os relacionamentos um para muitos exigem
uma propriedade de navegação de coleção em um dos lados.
No banco de dados o relacionamento um-para-muitos é formado quando uma linha da primeira tabela é vinculada a muitas linhas na segunda tabela, mas apenas uma linha da segunda tabela é vinculada a uma linha da primeira tabela.
Exemplo: Blog e Post , onde um blog pode ter muitos posts
1- Blog
public class Blog
{
public int BlogId { get; set; }
public string? Url { get; set; }
public List<Post>? Posts { get; set; }
}
|
2- Post
public class Post
{
public int PostId { get; set; }
public string? Titulo { get; set; }
public string? Conteudo { get; set; }
public Blog? Blog { get; set; }
}
|
Observe que a propriedade de navegação na classe Post retorna a referência ao objeto de Blog e que a propriedade de navegação na classe Blog retorna a coleção de objetos Post.
Em um relacionamento Um para Muitos, a chave primária da tabela de Blog (BlogId) é definida como a chave estrangeira na tabela Post.
Podemos também incluir a propriedade BlogId de forma explicita na classe dependente (Post) :
public class Post
{
public int PostId { get; set; }
public string? Titulo { get; set; }
public string? Conteudo { get; set; }
public int BlogId { get; set; }
public Blog? Blog { get; set; }
}
|
O EF Core vai gerar a propriedde BlogId como uma coluna Not Nullable. Se você desejar que esta coluna seja Nullable, defina a propriedade como Nullable conforme mostrado no código abaixo :
public int? BlogId { get; set; }
2- Relacionamento Many-to-Many
Os relacionamentos
um para muitos exigem uma propriedade de navegação de coleção em
ambos os lados das entidades.
Um relacionamento Muitos-para-Muitos ocorre quando várias linhas de uma tabela são vinculadas a várias linhas de outra tabela e vice-versa.
Exemplo: Funcionario e Projeto, onde um funcionário pode fazer parte de mais de um projeto e um projeto pode conter muitos funcionários.
1- Funcionario
public class Funcionario
{
public int FuncionarioId { get; set; }
public string? Nome { get; set; }
public virtual ICollection<Projeto>? Projetos { get; set; }
}
|
2- Projeto
public class Projeto
{
public int ProjetoId { get; set; }
public string? Nome { get; set; }
public float Prazo { get; set; }
public virtual ICollection<Funcionario>? Funcionarios { get; set; }
}
|
O relacionamento muitos para muitos geralmente envolve a criação de uma tabela de junção.
A tabela de junção terá uma chave primária composta que consiste na combinação da chave primária da tabela Funcionarios e da tabela Projetos.
Da versão 5.0 do EF Core em diante não precisamos definir uma entidade que vai atuar como a tabela de junção. Isso é feito de forma automatica pelo EF Core. Para o exemplo será criada a tabela de junção FuncionarioProjeto.
O uso da palavra-chave virtual apenas vai dar suporte ao Lazy Loading. Para isso basta incluir o pacote Microsoft.EntityFrameworkCore.Proxies e habilitar o recurso definindo UseLazyLoadingProxies.
Para os exemplos apresentados, após aplicar a migração, iremos obter o seguinte resultado no banco de dados SQL Server:
Note que temos a definição dos relacionamentos um-para-um entre Enderecos e Alunos, um-para-muitos entre Blogs e Posts e muitos-para-muitos entre Funcionarios e Projetos.
E usamos apenas as convenções padrão.
Em outro artigo vou mostrar como obter o mesmo resultado usando a Fluent API.
"Nada façais por contenda ou por vanglória, mas por
humildade; cada um considere os outros superiores a si mesmo."
Filipenses 2:3
Referências: