entity-framework-4.1 - ventajas - instalar entity framework visual studio 2017
Entidad 4.1 ActualizaciĆ³n de una entidad principal existente con nuevas entidades secundarias (4)
Primero borro la colección de ingredientes existente en la entidad del producto y luego agrego la lista actualizada de ingredientes nuevamente.
Bueno, este es un tipo de ataque de fuerza bruta para actualizar la colección secundaria. EF no tiene ninguna magia para actualizar a los niños, lo que significa: agregar nuevos hijos, eliminar los niños eliminados, actualizar los niños existentes, solo configurando el estado del padre como Modified
. Básicamente, este procedimiento lo obliga a eliminar también los hijos anteriores de la base de datos e insertar el nuevo, así:
// product is the detached product with the detached new children collection
using (var context = new MyContext())
{
var productInDb = context.Products.Include(p => p.Ingredients)
.Single(p => p.Id == product.Id);
// Update scalar/complex properties of parent
context.Entry(productInDb).CurrentValues.SetValues(product);
foreach (var ingredient in productInDb.Ingredients.ToList())
context.Ingredients.Remove(ingredient);
productInDb.Ingredients.Clear(); // not necessary probably
foreach (var ingredient in product.Ingredients)
productInDb.Ingredients.Add(ingredient);
context.SaveChanges();
}
El mejor procedimiento es actualizar la colección de hijos en la memoria sin eliminar todos los hijos de la base de datos:
// product is the detached product with the detached new children collection
using (var context = new MyContext())
{
var productInDb = context.Products.Include(p => p.Ingredients)
.Single(p => p.Id == product.Id);
// Update scalar/complex properties of parent
context.Entry(productInDb).CurrentValues.SetValues(product);
var ingredientsInDb = productInDb.Ingredients.ToList();
foreach (var ingredientInDb in ingredientsInDb)
{
// Is the ingredient still there?
var ingredient = product.Ingredients
.SingleOrDefault(i => i.Id == ingredientInDb.Id);
if (ingredient != null)
// Yes: Update scalar/complex properties of child
context.Entry(ingredientInDb).CurrentValues.SetValues(ingredient);
else
// No: Delete it
context.Ingredients.Remove(ingredientInDb);
}
foreach (var ingredient in product.Ingredients)
{
// Is the child NOT in DB?
if (!ingredientsInDb.Any(i => i.Id == ingredient.Id))
// Yes: Add it as a new child
productInDb.Ingredients.Add(ingredient);
}
context.SaveChanges();
}
Tengo una aplicación donde puedes crear un nuevo tipo de producto y agregarle a ese producto algunos ingredientes. El producto y los ingredientes son ambas entidades guardadas en una base de datos. La entidad del producto tiene una colección de entidades de ingredientes.
(versión simplificada)
public class Product
Public Sub New()
Me.Ingredients = New List(Of Ingredient)()
End Sub
Property Ingredients as ICollection(Of Ingredient)
end class
Cuando guardo el producto por primera vez, todo va bien: simplemente lo agrego al contexto y llamo a SaveChanges.
myDataContext.Products.Add(product)
myDataContext.SaveChanges()
Tanto el producto (padre) como los ingredientes (niños) se guardan y se vinculan entre sí. Todo está bien.
Sin embargo, cuando agrego / elimino un ingrediente a un producto existente, empiezo a tener problemas. Primero borro la colección de ingredientes existente en la entidad del producto y luego agrego la lista actualizada de ingredientes nuevamente (no reutilizo los ingredientes, agregue el momento). Luego cambio el estado de la entidad del producto a modificado y llamo a savechanges. Sin embargo, al cambiar de estado, obtengo la excepción " Ya existe un objeto con la misma clave en el ObjectStateManager ".
myDataContext.Entry(product).State = EntityState.Modified
Después de la búsqueda "un poco", descubrí que el problema es que todos los ingredientes tienen una clave principal de 0 (ya que aún no se han agregado) y cuando se cambia el estado de la entidad principal (producto), todas las entidades secundarias (ingredientes) ) se adjuntan al contexto con la clave de 0, lo que causa el problema ya que las claves ya no son únicas.
He estado buscando una solución pero no sé cómo resolver este problema. Intenté agregar los ingredientes al contexto antes de cambiar el estado, pero luego falta el vínculo entre el producto y los ingredientes ... ¿Cómo actualizo una entidad principal existente con nuevas entidades secundarias aún no agregadas?
Yo uso Entity Framework 4.1 y Code First.
¡Espero que puedas ayudarme!
Después de muchos meses luchando por entender todo este Entity Framework horrible, espero que esto pueda ayudar a alguien y no pasar por la frustración que he sufrido.
public void SaveOrder(SaleOrder order)
{
using (var ctx = new CompanyContext())
{
foreach (var orderDetail in order.SaleOrderDetails)
{
if(orderDetail.SaleOrderDetailId == default(int))
{
orderDetail.SaleOrderId = order.SaleOrderId;
ctx.SaleOrderDetails.Add(orderDetail);
}else
{
ctx.Entry(orderDetail).State = EntityState.Modified;
}
}
ctx.Entry(order).State = order.SaleOrderId == default(int) ? EntityState.Added : EntityState.Modified;
ctx.SaveChanges();
}
}
Encontré este article reciente en la extensión GraphDiff para DbContext.
Aparentemente es una variante genérica y reutilizable de la de .
Código de ejemplo:
using (var context = new TestDbContext())
{
// Update DBcompany and the collection the company and state that the company ''owns'' the collection Contacts.
context.UpdateGraph(company, map => map.OwnedCollection(p => p.Contacts));
context.SaveChanges();
}
En otros comentarios; Veo que el autor ha propuesto al equipo de EF usar su código en el número 864 Brindar un mejor soporte para trabajar con entidades desconectadas .
Supongo que esta es una solución más simple.
public Individual
{
.....
public List<Address> Addresses{get;set;}
}
//where base.Update from Generic Repository
public virtual void Update(T entity)
{
_dbset.Attach(entity);
_dataContext.Entry(entity).State = EntityState.Modified;
}
//overridden update
public override void Update(Individual entity)
{
var entry = this.DataContext.Entry(entity);
var key = Helper.GetPrimaryKey(entry);
var dbEntry = this.DataContext.Set<Individual>().Find(key);
if (entry.State == EntityState.Detached)
{
if (dbEntry != null)
{
var attachedEntry = this.DataContext.Entry(dbEntry);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
base.Update(entity);
}
}
else
{
base.Update(entity);
}
if (entity.Addresses.Count > 0)
{
foreach (var address in entity.Addresses)
{
if (address != null)
{
this.DataContext.Set<Address>().Attach(address);
DataContext.Entry(address).State = EntityState.Modified;
}
}
}
}