Many Relationship for Many Entity Framework 6

Asked

Viewed 6,973 times

7

Good morning, I have the following classes:

Consul_ca_student:

public class CONSUL_CA_Aluno
{
    public int Id { get; set; }
    public string Nome { get; set; }
    public int Cpf { get; set; }
    public string Email { get; set; }
    public string Senha { get; set; }
    public int Ativo { get; set; }

    public virtual ICollection<CONSUL_CA_Curso> CONSUL_CA_Cursos { get; set; }
}

Consul_ca_curso:

public class CONSUL_CA_Curso
{
    public int Id { get; set; }
    public string Nome { get; set; }
    public int Ativo { get; set; }
    public string Ministrante { get; set; }
    public string Duracao { get; set; }
    public int CargaHoraria { get; set; }
    public string LocalCurso { get; set; }

    public ICollection<CONSUL_CA_Aluno> CONSUL_CA_Alunos { get; set; }
}

In the database I have the table Consul_ca_cursoaluno where the class data will be stored.

When I test:

CONSUL_CA_Aluno aluno = new CONSUL_CA_Aluno();
aluno.Ativo = 1;
aluno.Cpf = 1321;
aluno.Email = "email";
aluno.Nome = "diididid";
aluno.Senha = "123";
aluno.CONSUL_CA_Cursos = contexto.Cursos.ToList();
aluno.CONSUL_CA_Cursos = aluno.CONSUL_CA_Cursos.Select(curso => contexto.Curso.FirstOrDefault(x => x.Id == curso.Id)).ToList();
contexto.Aluno.Add(aluno);
contexto.SaveChanges();

Displays the following error:

An unhandled Exception of type 'System.Data.Entity.Infrastructure.Dbupdateexception' occurred in Entityframework.dll
Additional information: An error occurred while saving entities that do not Expose Foreign key properties for their relationships. The Entityentries Property will Return null because a single Entity cannot be identified as the source of the Exception. Handling of exceptions while saving can be made easier by exposing Foreign key properties in your Entity types. See the Innerexception for Details.

  • Is there any need to use these encrypted names? CONSUL_CA_Aluno could be replaced only by Aluno, nay?

  • In the database these nomenclatures are used, so to maintain the standard, I used tbm!

  • Not necessary. You can use Attribute [Table("Consul_ca_student")] before class declaration Aluno that the Entity Framework points to you.

  • I didn’t. Right, but as for the error in insertion, some help?

  • I’m writing your answer. Just a moment.

2 answers

8


The Entity Framework is lost when defining the association N to N, even if in theory the declaration of its Model be correct.

There are two ways to solve:

1. Using Fluent Configuration in Model Builder

modelBuilder.Entity<Aluno>()
        .HasMany(a => a.Cursos)
        .WithMany()
        .Map(x =>
        {
            x.MapLeftKey("AlunoId");
            x.MapRightKey("CursoId");
            x.ToTable("AlunosCursos");
        });

The problem with this approach is that the associative table is minimal. You cannot add extra fields related to the association. The change is minor but will work for the code posted in the question.

2. Defining an associative entity

[Table("CONSUL_CA_Aluno")]
public class Aluno
{
    [Key]
    public int AlunoId { get; set; }
    [Required]
    public string Nome { get; set; }
    [Required]
    public int Cpf { get; set; }
    [Required]
    [EMailAddress]
    public string Email { get; set; }
    public string Senha { get; set; }
    [DefaultValue(true)]
    public Boolean Ativo { get; set; } /* troquei este para Boolean */

    public virtual ICollection<AlunoCurso> AlunosCursos { get; set; }
}

[Table("CONSUL_CA_Cursos")]
public class Curso 
{
    [Key]
    public int CursoId { get; set; }
    [Required]
    public string Nome { get; set; }
    [DefaultValue(true)]
    public Boolean Ativo { get; set; } /* troquei este para Boolean */
    public string Ministrante { get; set; }
    public string Duracao { get; set; }
    public int CargaHoraria { get; set; }
    public string LocalCurso { get; set; }

    public ICollection<AlunoCurso> AlunosCursos { get; set; }
}

public class AlunosCursos
{
    [Key]
    public int AlunoCursoId { get; set; }
    public int AlunoId { get; set; }
    public int CursoId { get; set; }

    public virtual Aluno Aluno { get; set; }
    public virtual Curso Curso { get; set; }
}

No Controller

Aluno aluno = new Aluno();
// A linha de baixo não precisa colocar porque eu já defini o default no Model
// aluno.Ativo = true;
aluno.Cpf = 1321;
aluno.Email = "email";
aluno.Nome = "diididid";
aluno.Senha = "123";

contexto.Aluno.Add(aluno);
contexto.SaveChanges();

foreach (var curso in contexto.Cursos.ToList()) {
    var alunoCurso = new AlunoCurso();
    alunoCurso.Aluno = aluno;
    alunoCurso.Curso = curso;
    contexto.AlunosCursos.Add(alunoCurso);
    contexto.SaveChanges();
}

contexto.Entry(aluno).State = EntityState.Modified;
contexto.SaveChanges();

In this case, your code should be changed to deal with this association. It gets a little more bureaucratic, but it’s a more complete approach.

  • For the second option, you know some tutorial, or something I could follow. Because I don’t know how I could handle this option.

  • @Diegozanardo Want me to put an example of code? So you don’t lose the thread.

  • I do. If possible.

  • @Diegozanardo I put how to do in Controller. See if it answers.

  • Doubt: Don’t Alunoid and Cursoid need to be set in the controller? And every time there is a change in student x course it is necessary to delete all records related to that student and enter all again?

  • @Diegozanardo Not recommended. Context can get lost and cause duplicate insertion of objects. Use the object directly to follow the Entity Framework convention and avoid future problems.

  • I don’t understand your answer!

Show 2 more comments

3

You’re wrong here:

aluno.CONSUL_CA_Cursos = aluno.CONSUL_CA_Cursos.Select(curso => contexto.Curso.FirstOrDefault(x => x.Id == curso.Id)).ToList();

Are you using the aluno which is a new object to seek information, would not be the context? , ie the class it inherited from DbContext?

Example of use

[Table("Livro")]
public class Livro
{
    public Livro()
    {
         this.Autores = new HashSet<Autor>();  
    }

    [Key()]
    [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public String Titulo { get; set; }

    public virtual ICollection<Autor> Autores { get; set;}
}

[Table("Autor")]
public class Autor
{
    public Autor()
    {
        this.Livros = new HashSet<Livro>();
    }

    [Key()]
    [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public String Nome { get; set; }

    public virtual ICollection<Livro> Livros {get;set;}
}

These entities have relationship N - M that is, many to many.


public class Context: DbContext
{
            public Context()
                : base("conexao") { }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Livro>()
                        .HasMany(l => l.Autores)
                        .WithMany(a => a.Livros)
                        .Map(x =>
                        {
                            x.MapLeftKey("LivroId");
                            x.MapRightKey("AutorId");
                            x.ToTable("LivroAutor");
                        });
                base.OnModelCreating(modelBuilder);
            }
            public DbSet<Livro> Livro { get; set; }
            public DbSet<Autor> Autor { get; set; }
}

//classe que herda de DbContext // EF6
Context db = new Context();

//Livro (Novo);
Livro livro = new Livro();
livro.Titulo = "Livro 1";


//Autor (Novo);
Autor autor = new Autor();
autor.Nome = "Autor 1";
// adicionando esse livro nesse autor muitos para muitos aqui ...
autor.Livros.Add(livro); 

//adicionando livro
db.Livro.Add(livro);

//adicionando autor
db.Autor.Add(autor);

//salvando todas alterações
db.SaveChanges();

db.Dispose();

This is a layout of many for many, now if the intermediate table is different it has to be exposed in Context (DbSet<>) for the passage of the additional values.

  • Do you say that? Consul_ca_courses = context.Aluno.Select(course => context.Curso.Firstordefault(x => x.Id == course.Id)). Tolist();

  • Yes, that’s what he meant

  • I tested it here, and it didn’t work!

  • Yes ... student would be a new instance understood ... if you have to take the courses of the class that inherits Dbcontext

  • It didn’t work. Same mistake!

  • I put an example book and author where book can have several authors and authors can contain several books (N - M), if you want to watch Diego !!!

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.