Quando estamos modelando o banco de dados no Entity Framework Core (ou em qualquer outra ferramenta) é muito comum criarmos o erro cíclico. Este erro ocorre devido ao banco de dados não conseguir determinar onde o relacionamento termina, causando um erro de loop. Vejamos uma situação real onde foi gerado esse problema: Um Professor leciona em várias Turmas, Um Professor leciona uma ou mais Disciplinas, Um Professor leciona determinadas Disciplinas em determinadas Turmas, Uma Turma tem muitos Professores, Em todas as Turmas tem 9 Disciplinas. Em todas as turmas podem ter até 9 Professores. Só por essa descrição já é possível perceber que os relacionamentos podem ser complexos de entender. E tem-se também o problema da navegação em via dupla: Professor-Turmas, Turma-Professores.
Então, como modelar essas tabelas sem que o banco entre em loop?
Para resolver esse problema no Entity Framework Core e evitar o erro de "Multiple Cascade Paths" (caminhos múltiplos de cascata) comum no MSSQL, a melhor abordagem é criar uma Entidade de Ligação Ternária.
Em vez de tentar conectar as três classes diretamente entre si, criamos uma quarta classe que representa a "Alocação" ou o "Horário" do professor.
1. Modelagem das Entidades
Vamos criar a classe ProfessorDisciplinaTurma (ou um nome como GradeCurricular). Ela será o ponto central da relação.
public class Professor
{
public int Id { get; set; }
public string Nome { get; set; }
// Relacionamento com a tabela de ligação
public ICollection<ProfessorDisciplinaTurma> Vinculos { get; set; }
}
public class Turma
{
public int Id { get; set; }
public string Nome { get; set; }
public ICollection<ProfessorDisciplinaTurma> Vinculos { get; set; }
}
public class Disciplina
{
public int Id { get; set; }
public string Nome { get; set; }
public ICollection<ProfessorDisciplinaTurma> Vinculos { get; set; }
}
// A ENTIDADE DE LIGAÇÃO
public class ProfessorDisciplinaTurma
{
public int Id { get; set; }
public int ProfessorId { get; set; }
public Professor Professor { get; set; }
public int TurmaId { get; set; }
public Turma Turma { get; set; }
public int DisciplinaId { get; set; }
public Disciplina Disciplina { get; set; }
}
2. Configuração do Fluent API (Evitando o Ciclo)
O SQL Server não permite que uma única tabela tenha múltiplos caminhos de "Delete Cascade". Se você deletar um Professor, ele tentaria deletar a ligação; se deletar a Turma, também. Isso gera o erro de ciclo.
Para resolver, precisamos configurar o comportamento de deleção como Restrict ou NoAction no DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProfessorDisciplinaTurma>(entity =>
{
// Chave primária simples ou composta
entity.HasKey(pdt => pdt.Id);
// Relacionamento com Professor
entity.HasOne(pdt => pdt.Professor)
.WithMany(p => p.Vinculos)
.HasForeignKey(pdt => pdt.ProfessorId)
.OnDelete(DeleteBehavior.Restrict); // Evita o ciclo
// Relacionamento com Turma
entity.HasOne(pdt => pdt.Turma)
.WithMany(t => t.Vinculos)
.HasForeignKey(pdt => pdt.TurmaId)
.OnDelete(DeleteBehavior.Restrict); // Evita o ciclo
// Relacionamento com Disciplina
entity.HasOne(pdt => pdt.Disciplina)
.WithMany(d => d.Vinculos)
.HasForeignKey(pdt => pdt.DisciplinaId)
.OnDelete(DeleteBehavior.Restrict); // Evita o ciclo
});
}
Por que isso funciona?
Sem Ambiguidade: O banco de dados agora sabe exatamente que
ProfessorDisciplinaTurmaé a tabela que une os três conceitos.Quebra do Ciclo de Cascade: Ao usar
.OnDelete(DeleteBehavior.Restrict), você diz ao SQL Server: "Se eu deletar um Professor que tem turmas vinculadas, não delete os vínculos automaticamente (ou me impeça de deletar)". Isso remove a árvore de decisão complexa que causa o erro de ciclo.Flexibilidade: Você pode adicionar campos extras nessa tabela de ligação, como "Ano Letivo" ou "Semestre", o que é muito comum nesse tipo de sistema acadêmico.
Como consultar esses dados?
Para buscar quais disciplinas um professor leciona em determinada turma, você usaria o Include:
var dados = context.ProfessorDisciplinaTurma
.Include(pdt => pdt.Professor)
.Include(pdt => pdt.Disciplina)
.Include(pdt => pdt.Turma)
.Where(pdt => pdt.ProfessorId == idProfessor)
.ToList();
Você gostaria que eu ajudasse a estruturar uma consulta específica para listar os alunos dessa turma junto com o professor?
Comentários