Actualización del Entity Framework con una entidad relacionada

asp.net entity-framework foreign-keys

Pregunta

Estoy usando el EF para intentar actualizar una entidad con ASP.NET. Estoy creando una entidad, configurando sus propiedades y luego pasándolas de vuelta al EF en una capa separada con el ID para que se pueda aplicar el cambio. Estoy haciendo esto porque solo almaceno el ID de la entidad cuando se ha vinculado a los controles de IU.

Todo funciona para las propiedades estándar, pero no puedo actualizar la Category.ID de un Producto (una entidad relacionada). He intentado EntityKey, EntityReference y algunos otros, pero el ID de categoría no está guardado. Esto es lo que tengo:

Product product = new Product();
product.CategoryReference.EntityKey = new EntityKey("ShopEntities.Categories", "CategoryID", categoryId);
product.Name = txtName.Text.Trim();
... other properties
StockControlDAL.EditProduct(productId, product);

public static void EditProduct(int productId, Product product) {
 using(var context = new ShopEntities()) {
     var key = new EntityKey("ShopEntities.Products", "ProductID", productId);
     context.Attach(new Product() { ProductID = productId, EntityKey = key });
     context.AcceptAllChanges();
     product.EntityKey = key;
     product.ProductID = productId;
     context.ApplyPropertyChanges("ShopEntities.Products", product);
     context.SaveChanges();
 }
}

Tengo muchas ganas de usar el EF, pero parece que tengo algunos problemas con su uso con ASP.NET.

Respuesta aceptada

Esta es una respuesta aceptada a esta pregunta. MVC de ASP.NET con Entidad Framework

context.AttachTo(product.GetType().Name, product);
ObjectStateManager stateMgr = context.ObjectStateManager;
ObjectStateEntry stateEntry = stateMgr.GetObjectStateEntry(model);
stateEntry.SetModified();
context.SaveChanges();

¿Has probado eso?

[Actualizado, el código en la parte superior no funciona]

Esta es una pequeña propiedad de extensión que usé para que el siguiente bloque de código sea más fácil de entender:

public partial class Product
{
    public int? CategoryID
    {
        set
        {  
           CategoryReference.EntityKey = new EntityKey("ShopEntities.Categories", "CategoryID", value);
        }
        get
        {
            if (CategoryReference.EntityKey == null)
                return null;

            if (CategoryReference.EntityKey.EntityKeyValues.Count() > 0)
                return (int)CategoryReference.EntityKey.EntityKeyValues[0].Value;
            else
                return null;
        }
    }
}

y eso me funcionó (esta vez seguro):

System.Data.EntityKey key = new System.Data.EntityKey("ShopEntities.Products", "ProductID", productId);
        object originalItem;   

        product.EntityKey = key;
        if (context.TryGetObjectByKey(key, out originalItem))
        {
            if (originalItem is EntityObject &&
                ((EntityObject)originalItem).EntityState != System.Data.EntityState.Added)
            {
                Product origProduct = originalItem as Product;   
                origProduct.CategoryID == product.CategoryID;//set foreign key again to change the relationship status           
                context.ApplyPropertyChanges(
                    key.EntitySetName, product);

            }
        }context.SaveChanges();

Por supuesto que se ve hacky. Creo que la razón se debe a que las relaciones de EF tienen un estado como entidades (modificado, agregado, eliminado) y, en función de ese estado, EF cambia el valor de las claves externas o elimina la fila si hay muchas o muchas relaciones en el caso. Por alguna razón (no sé por qué) el estado de la relación no se cambia igual que el estado de la propiedad. Es por eso que tuve que configurar la CategoryReference.EntityKey en originalItem para cambiar el estado de la relación.


Respuesta popular

La razón por la que esto falla es doble.

  1. Para actualizar una referencia (es decir, Product.Category) debe tener también el valor de referencia original en el contexto.
  2. ApplyPropertyChanges (...) solo se aplica a las propiedades regulares / escalares de la Entidad, la referencia no se modifica

Así que haría algo como esto (tenga en cuenta que este código hace un uso intensivo de un truco llamado entidades de código auxiliar para evitar mezclarse con EntityKeys)

Product product = new Product();
// Use a stub because it is much easier.
product.Category = new Category {CategoryID = selectedCategoryID};
product.Name = txtName.Text.Trim();
... other properties

StockControlDAL.EditProduct(productId, originalCategoryID);


public static void EditProduct(Product product, int originalCategoryID ) {
 using(var context = new ShopEntities()) 
 {
     // Attach a stub entity (and stub related entity)
     var databaseProduct = new Product { 
             ProductID = product.ProductID, 
             Category = new Category {CategoryID = originalCategoryID}
         };
     context.AttachTo("Products", databaseProduct);

     // Okay everything is now in the original state
     // NOTE: No need to call AcceptAllChanges() etc, because 
     // Attach puts things into ObjectContext in the unchanged state

     // Copy the scalar properties across from updated product 
     // into databaseProduct in the ObjectContext
     context.ApplyPropertyChanges("ShopEntities.Products", product);

     // Need to attach the updated Category and modify the 
     // databaseProduct.Category but only if the Category has changed. 
     // Again using a stub.
     if (databaseProduct.Category.CategoryID != product.Category.CategoryID)
     {
         var newlySelectedCategory = 
                 new Category {
                     CategoryID = product.Category.CategoryID
                 };

         context.AttachTo("Categories", newlySelectedCategory)

         databaseProduct.Category = newlySelectedCategory;

     }

     context.SaveChanges();
 }
}

Esto hará el trabajo, asumiendo que no hay errores tipográficos, etc.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué