Insertar / actualizar muchos a muchos Entity Framework. ¿Cómo lo hago?

104

Estoy usando EF4 y soy nuevo en él. Tengo muchos a muchos en mi proyecto y parece que no puedo resolver cómo insertar o actualizar. He creado un pequeño proyecto solo para ver cómo debería codificarse.

Supongamos que tengo 3 mesas

  1. Clase: ClassID-ClassName
  2. Estudiante: StudentID-FirstName-Apellidos
  3. StudentClass: StudentID-ClassID

Después de agregar toda la relación y actualizar el modelo a través del navegador de modelos, noté que StudentClass no aparece, este parece ser el comportamiento predeterminado.

Ahora necesito hacer una inserción y una actualización. ¿Cómo lo haces? ¿Alguna muestra de código o enlace donde pueda descargar un ejemplo, o puede dedicar 5 minutos?

user9969
fuente

Respuestas:

139

En términos de entidades (u objetos), tiene un Classobjeto que tiene una colección de Studentsy un Studentobjeto que tiene una colección de Classes. Dado que su StudentClasstabla solo contiene los ID y ninguna información adicional, EF no genera una entidad para la tabla de unión. Ese es el comportamiento correcto y eso es lo que esperas.

Ahora, cuando haga inserciones o actualizaciones, intente pensar en términos de objetos. Por ejemplo, si desea insertar una clase con dos estudiantes, cree el Classobjeto, los Studentobjetos, agregue los estudiantes a la Studentscolección de la clase , agregue el Classobjeto al contexto y llame SaveChanges:

using (var context = new YourContext())
{
    var mathClass = new Class { Name = "Math" };
    mathClass.Students.Add(new Student { Name = "Alice" });
    mathClass.Students.Add(new Student { Name = "Bob" });

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

Esto creará una entrada en la Classtabla, dos entradas en la Studenttabla y dos entradas en la StudentClasstabla uniéndolas.

Básicamente, haces lo mismo con las actualizaciones. Simplemente obtenga los datos, modifique el gráfico agregando y eliminando objetos de las colecciones, llame SaveChanges. Consulte esta pregunta similar para obtener más detalles.

Editar :

De acuerdo con su comentario, debe insertar uno nuevo Classy agregar dos existentes Students:

using (var context = new YourContext())
{
    var mathClass= new Class { Name = "Math" };
    Student student1 = context.Students.FirstOrDefault(s => s.Name == "Alice");
    Student student2 = context.Students.FirstOrDefault(s => s.Name == "Bob");
    mathClass.Students.Add(student1);
    mathClass.Students.Add(student2);

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

Dado que ambos estudiantes ya están en la base de datos, no se insertarán, pero como ahora están en la Studentscolección de Class, se insertarán dos entradas en la StudentClasstabla.

Yakimych
fuente
Hola, gracias por su respuesta. Mi escenario es que necesito insertar 1 entrada en la clase y como x su ejemplo 2 entradas en StudentClass y ninguna entrada en Student. Estoy confundido sobre cómo lo hago
user9969
ESO FUNCIONÓ UN TRATO. Con respecto a la actualización, ¿hay algo especial que deba hacer? Por ejemplo, solo actualizar el className, por ejemplo.
user9969
3
Esto agregará una sobrecarga de obtener de la base de datos los elementos que se deben agregar. El método Attach se puede usar para agregar solo una relación. Consulte msdn.microsoft.com/en-us/data/jj592676.aspx y también stackoverflow.com/questions/11355019/…
Gnomo
3
AddToClasses es el DbSet para Class?
Jo Smo
1
No olvide inicializar sus colecciones en los constructores de Class y Student. Por ejemplo: public Class () {this.Students = new HashSet <Estudiante> (); }
user1040323
40

Pruebe este para actualizar:

[HttpPost]
public ActionResult Edit(Models.MathClass mathClassModel)
{
    //get current entry from db (db is context)
    var item = db.Entry<Models.MathClass>(mathClassModel);

    //change item state to modified
    item.State = System.Data.Entity.EntityState.Modified;

    //load existing items for ManyToMany collection
    item.Collection(i => i.Students).Load();

    //clear Student items          
    mathClassModel.Students.Clear();

    //add Toner items
    foreach (var studentId in mathClassModel.SelectedStudents)
    {
        var student = db.Student.Find(int.Parse(studentId));
        mathClassModel.Students.Add(student);
    }                

    if (ModelState.IsValid)
    {
       db.SaveChanges();
       return RedirectToAction("Index");
    }

    return View(mathClassModel);
}
Stritof
fuente
esto salvó mi día gracias !!! la parte clave es el item.Collection (i => i.Students) .Load (); parte
Gerrie Pretorius
Solo una nota al margen, tuve una InvalidOperationException al hacer eso, algo como que mi entidad no existía en el contexto. Acabo de llamar a context.MyEntityCollection.Attach (myEntity) para solucionarlo.
Kilazur
Nunca me hubiera dado cuenta de eso. Gracias una tonelada.
Playa Jared
1
Permítame agregar mi agradecimiento aquí. Escribí y probé más de 130 líneas de código durante los últimos 4 días tratando de realizar esta tarea pero no pude hacerlo. Probé esto y las cosas funcionaron perfectamente. Si bien no entiendo el propósito de establecer la variable de elemento y luego no usarla en absoluto, ¡me alegro de que funcione! ¡¡¡Un millón de gracias bajillion !!!! :)
Dude-Dastic
Considerando que la pregunta es Insertar Y Actualizar, esta respuesta completa la pregunta y la respuesta aceptada. ¡Muchas gracias!
Vahx
5

Quería agregar mi experiencia a eso. De hecho, EF, cuando agrega un objeto al contexto, cambia el estado de todos los elementos secundarios y entidades relacionadas a Agregado. Aunque hay una pequeña excepción en la regla aquí: si los niños / entidades relacionadas están siendo rastreados por el mismo contexto, EF entiende que estas entidades existen y no las agrega. El problema ocurre cuando, por ejemplo, carga los niños / entidades relacionadas de algún otro contexto o una interfaz de usuario web, etc. y luego sí, EF no sabe nada sobre estas entidades y las agrega todas. Para evitar eso, simplemente obtenga las claves de las entidades y búsquelas (por ejemplo, context.Students.FirstOrDefault(s => s.Name == "Alice"))en el mismo contexto en el que desea hacer la adición.

Athina
fuente
3

Utilizo la siguiente forma para manejar la relación de muchos a muchos donde solo están involucradas claves externas.

Entonces para insertar :

public void InsertStudentClass (long studentId, long classId)
{
    using (var context = new DatabaseContext())
    {
        Student student = new Student { StudentID = studentId };
        context.Students.Add(student);
        context.Students.Attach(student);

        Class class = new Class { ClassID = classId };
        context.Classes.Add(class);
        context.Classes.Attach(class);

        student.Classes = new List<Class>();
        student.Classes.Add(class);

        context.SaveChanges();
    }
}

Para borrar ,

public void DeleteStudentClass(long studentId, long classId)
{
    Student student = context.Students.Include(x => x.Classes).Single(x => x.StudentID == studentId);

    using (var context = new DatabaseContext())
    {
        context.Students.Attach(student);
        Class classToDelete = student.Classes.Find(x => x.ClassID == classId);
        if (classToDelete != null)
        {
            student.Classes.Remove(classToDelete);
            context.SaveChanges();
        }
    }
}
lenglei
fuente
1

En el marco de la entidad, cuando el objeto se agrega al contexto, su estado cambia a Agregado. EF también cambia el estado de cada objeto para agregarlo en el árbol de objetos y, por lo tanto, obtiene un error de violación de clave principal o se agregan registros duplicados en la tabla.

Nilesh Hirapra
fuente
2
Por favor edición para dar suficientes detalles para que un usuario no tiene que visitar su blog para obtener la respuesta.