EF Core - Definindo e atualizando o relacionamento muitos-para-muitos - I


Hoje como definir e atualizar o relacionamento muitos-para-muitos usando o EF Core.

O relacionamento muitos-para-muitos é implementado como dois relacionamentos um-para-muitos e uma tabela de junção.

A figura a seguir descreve um relacionamento muitos-para-muitos entre as tabelas Livros e Autores onde a tabela de junção usada é a tabela LivroAutor:

Assim, a tabela LivroAutor é a chave para criar um relacionamento muitos para muitos.

A figura a seguir descreve com mais detalhes este relacionamento exibindo as chaves primárias e estrangeiras usadas:

Aqui a notação PK significa Primary Key ou chave primário e FK significa foreign key ou chave estrangeira.

A tabela de junção - LivroAutor -  possui chaves estrangeiras (FK) que se ligam à(s) chave(s) primária(s) (PK) de cada extremidade do relacionamento - neste caso, a chave LivroId do livro e a chave AutorId da entidade Autor.

Essas chaves estrangeiras, em seguida, formam uma chave primária composta para a tabela LivroAutor.

Vamos definir a seguir em um projeto Console do tipo .NET Core (versão 2.2), criado no VS 2019, as entidades envolvidades na definição deste relacionamento.

No projeto Console devemos instalar os seguintes pacotes: (estou usando a última versão estável)

A seguir temos o código das entidades usadas no projeto:

1- Livro

public class Livro
{
   public int LivroId { get; set; }
   public string Titulo { get; set; }
   public DateTime Lancamento { get; set; }
   public string ISBN { get; set; }
   public decimal Preco { get; set; }
   public ICollection<LivroAutor> LivrosAutores {get; set;}
}

2- Autor

public class Autor
{
   public int AutorId { get; set; }
   public string Nome { get;set }
   public string SobreNome { get; set; }
   public string Pais { get; set; } 
   public ICollection<LivroAutor> LivrosAutores { get; set; }
}

No EF Core, é necessário incluir uma entidade no modelo para representar a tabela de junção e, em seguida, adicionar propriedades de navegação a um dos lados das relações muitos-para-muitos que apontam para a entidade de junção:

3- LivroAutor

public class LivroAutor
{
   public int LivroId { get; set; }
   public int AutorId { get; set; }

   public Livro Livro { get; set; }
   public Autor Autor { get; set; } 
}

A forma como definimos as entidades atribuindo os nomes das propriedades para as chaves primárias e definindo as propriedades de navegação foi feita de propósito para que o EF Core encontre os relacionamentos usando as regras de convenção.

Assim usamos os nomes LivroId e AutorId para as chaves primárias na classe Livro e Autor e o EF Core vai entender que essas propriedades serão as chaves primárias nas tabelas e então ele infere o relacionamento.

O EF Core só não fai conseguir descobrir a chave primária da tabela LivroAutor pois ela não segue a convenção padrão, e, para fazer isso vamos usar a Fluente API e definir o código abaixo no método OnModelCreating na classe de contexto AppDbContext:

using Microsoft.EntityFrameworkCore;
namespace EFCore_Muitos_Muitos
{
    public class AppDbContext : DbContext
    {
        public DbSet<Livro> Livros { get; set; }
        public DbSet<Autor> Autores { get; set; }
        public DbSet<LivroAutor> LivrosAutores { get; set; }
        private readonly string _connectionString;
        public AppDbContext()
        {
             _connectionString = @"Data Source=Macoratti;Initial Catalog=DemoDB;Integrated Security=True";
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(_connectionString);
        }

        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<LivroAutor>()
                .HasKey(x => new { x.LivroId, x.AutorId });
        }
    }
}

Observe que o método HasKey usado esta definindo uma chave composta que consiste de : LivroId e AutorId.

Esta é a abordagem mais simples e fácil para definir o relacionamento muitos para muitos mas podemos também definir explicitamente os relacionamentos usando os comando da Fluent API e os métodos HasOne/HasMany.

Nesta abordagem vamos alterar o código usado no método OnModelCreating que fica assim:

using Microsoft.EntityFrameworkCore;
namespace EFCore_Muitos_Muitos
{
    public class AppDbContext : DbContext
    {
        ...
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<LivroAutor>()
                .HasKey(x => new { x.LivroId, x.AutorId });
            //definindo o relacionamento explicitamente
            modelBuilder.Entity<LivroAutor>()
            .HasOne(bc => bc.Livro)
             .WithMany(b => b.LivrosAutores)
              .HasForeignKey(bc => bc.LivroId);
                modelBuilder.Entity<LivroAutor>()
                    .HasOne(bc => bc.Autor)
                    .WithMany(c => c.LivrosAutores)
                     .HasForeignKey(bc => bc.AutorId);
        }
    }
}

A chave primária para a tabela de junção - LivroAutor -  é uma chave composta que compreende ambos os valores de chave estrangeira. Além disso, ambos os lados do relacionamento muitos-para-muitos são configurados usando os métodos HasOne, WithMany e HasForeignKey da Fluent API.

Executando os comandos da migração:

- add-migration inicial

- update-database

Iremos obter as tabelas exibidas conforme abaixo no diagrama de relacionamento :

Vemos assim o relacionamento muitos-para-muitos definido via EF Core e na próxima parte do artigo veremos como incluir e atualizar dados.

Pegue o código usado no projeto aqui: EFCore_Muitos_Muitos.zip

Ninguém jamais viu a Deus; o Deus unigênito (Jesus), que está no seio do Pai, é quem o revelou.
João 1:17,18

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

Referências:


José Carlos Macoratti