Marco de la entidad, problemas para actualizar objetos relacionados

Actualmente estoy trabajando en un proyecto que utiliza la última versión de Entity Framework y me he encontrado con un problema que parece que no puedo resolver.

Cuando se trata de actualizar objetos existentes, puedo actualizar fácilmente las propiedades del objeto, hasta que se trata de una propiedad que es una referencia a otra clase.

En el ejemplo siguiente, tengo una clase llamada Foo, que almacena varias propiedades, siendo 2 de ellas instancias de otras clases

public class Foo { public int Id {get; set;} public string Name {get; set;} public SubFoo SubFoo {get; set} public AnotherSubFoo AnotherSubFoo {get; set} } 

Cuando uso el método Edit() , paso el objeto que deseo actualizar y puedo lograr que el Name se actualice correctamente, sin embargo, no he logrado encontrar la manera de obtener las propiedades del SubFoo. cambio. Por ejemplo, si la clase SubFoo tiene una propiedad de Name , y esto se ha cambiado y es diferente entre mi DB y el newFoo , no se actualiza.

 public Foo Edit(Foo newFoo) { var dbFoo = context.Foo .Include(x => x.SubFoo) .Include(x => x.AnotherSubFoo) .Single(c => c.Id == newFoo.Id); var entry = context.Entry(dbFoo); entry.OriginalValues.SetValues(dbFoo); entry.CurrentValues.SetValues(newFoo); context.SaveChanges(); return newFoo; } 

Cualquier ayuda o sugerencias serán bienvenidos.

ACTUALIZACIÓN: Basado en el comentario de Slauma, he modificado mi método para

 public Foo Edit(Foo newFoo) { var dbFoo = context.Foo .Include(x => x.SubFoo) .Include(x => x.AnotherSubFoo) .Single(c => c.Id == newFoo.Id); context.Entry(dbFoo).CurrentValues.SetValues(newFoo); context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo); context.SaveChanges(); return newFoo; } 

Cuando ejecuto esto ahora, obtengo el error:

El tipo de entidad Collection`1 no es parte del modelo para el contexto actual.

Para tratar de evitar esto, agregué código para intentar adjuntar las newFoo subclases de newFoo al contexto, pero esto a través de un error que decía que el ObjectManager ya tenía una entidad igual:

Ya existe un objeto con la misma clave en ObjectStateManager. ObjectStateManager no puede rastrear múltiples objetos con la misma clave

CurrentValues.SetValues solo actualiza las propiedades escalares pero no las entidades relacionadas, por lo que debe hacer lo mismo para cada entidad relacionada:

 public Foo Edit(Foo newFoo) { var dbFoo = context.Foo .Include(x => x.SubFoo) .Include(x => x.AnotherSubFoo) .Single(c => c.Id == newFoo.Id); context.Entry(dbFoo).CurrentValues.SetValues(newFoo); context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo); context.Entry(dbFoo.AnotherSubFoo).CurrentValues.SetValues(newFoo.AnotherSubFoo); context.SaveChanges(); return newFoo; } 

Si la relación se pudo haber eliminado por completo o se ha creado, también debe manejar esos casos explícitamente:

 public Foo Edit(Foo newFoo) { var dbFoo = context.Foo .Include(x => x.SubFoo) .Include(x => x.AnotherSubFoo) .Single(c => c.Id == newFoo.Id); context.Entry(dbFoo).CurrentValues.SetValues(newFoo); if (dbFoo.SubFoo != null) { if (newFoo.SubFoo != null) { if (dbFoo.SubFoo.Id == newFoo.SubFoo.Id) // no relationship change, only scalar prop. context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo); else { // Relationship change // Attach assumes that newFoo.SubFoo is an existing entity context.SubFoos.Attach(newFoo.SubFoo); dbFoo.SubFoo = newFoo.SubFoo; } } else // relationship has been removed dbFoo.SubFoo = null; } else { if (newFoo.SubFoo != null) // relationship has been added { // Attach assumes that newFoo.SubFoo is an existing entity context.SubFoos.Attach(newFoo.SubFoo); dbFoo.SubFoo = newFoo.SubFoo; } // else -> old and new SubFoo is null -> nothing to do } // the same logic for AnotherSubFoo ... context.SaveChanges(); return newFoo; } 

Eventualmente también necesitará establecer el estado de las entidades adjuntas a Modified si la relación ha sido modificada y las propiedades escalares también.

Editar

Si, de acuerdo con su comentario, Foo.SubFoo es en realidad una colección y no solo una referencia, necesitará algo como esto para actualizar las entidades relacionadas:

 public Foo Edit(Foo newFoo) { var dbFoo = context.Foo .Include(x => x.SubFoo) .Include(x => x.AnotherSubFoo) .Single(c => c.Id == newFoo.Id); // Update foo (works only for scalar properties) context.Entry(dbFoo).CurrentValues.SetValues(newFoo); // Delete subFoos from database that are not in the newFoo.SubFoo collection foreach (var dbSubFoo in dbFoo.SubFoo.ToList()) if (!newFoo.SubFoo.Any(s => s.Id == dbSubFoo.Id)) context.SubFoos.Remove(dbSubFoo); foreach (var newSubFoo in newFoo.SubFoo) { var dbSubFoo = dbFoo.SubFoo.SingleOrDefault(s => s.Id == newSubFoo.Id); if (dbSubFoo != null) // Update subFoos that are in the newFoo.SubFoo collection context.Entry(dbSubFoo).CurrentValues.SetValues(newSubFoo); else // Insert subFoos into the database that are not // in the dbFoo.subFoo collection dbFoo.SubFoo.Add(newSubFoo); } // and the same for AnotherSubFoo... db.SaveChanges(); return newFoo; } 

Solo pensé en publicar el enlace a continuación, ya que realmente me ayudó a entender cómo actualizar las entidades relacionadas.

Actualización de datos relacionados con el marco de la entidad en una aplicación asp net mvc

NOTA: Cambié ligeramente la lógica que se muestra en la función UpdateInstructorCourses para satisfacer mis necesidades.

También puede llamar a las tablas de forma independiente

 MyContext db = new MyContext // I like using asynchronous calls in my API methods var OldFoo = await db.Foo.FindAsync(id); var OldAssociateFoo = db.AssociatedFoo; var NewFoo = OldFoo; var NewAssociatedFoo = OldAssociatedFoo; NewFoo.SomeValue = "The Value"; NewAssociatedFoo.OtherValue = 20; db.Entry(OldFoo).CurrentValues.SetValues(NewFoo); db.Entry(OldAssociatedFoo).CurrentValues.SetValues(NewAssociatedFoo); await db.SaveChangesAsync();