使用Entity Framework更新相关实体

3
我正在使用EF尝试使用ASP.NET更新实体。我创建一个实体,设置其属性,然后将其带有ID的实体传递回到另一层中的EF,以便可以应用更改。我之所以这样做,是因为当将实体绑定到UI控件时,我只存储实体的ID。
所有标准属性都可以正常工作,但我无法更新Product的Category.ID(相关实体)。我尝试了EntityKey、EntityReference和其他一些方法,但类别ID未保存。这是我的代码:
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();
 }
}

我非常希望使用EF,但在与ASP.NET一起使用时似乎遇到了一些问题。

2个回答

6
这种方法失败的原因有两个。
  1. 为了更新引用(即Product.Category),您还必须在上下文中拥有原始引用值。
  2. ApplyPropertyChanges(...)仅适用于实体的常规/标量属性,引用保持不变。
因此,我会像这样做(请注意,此代码大量使用称为stub entities的技巧,以避免与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();
 }
}

假设没有错别字等问题,这将完成工作。


谢谢,两个解决方案都可以,但我只能标记一个答案。作为应用程序的一部分,我需要更新一个分离的实体列表(某些实体将具有子项列表(例如订单)而不是一个类别)。我看不出如何将此解决方案适应这种情况。这可能吗? - Echilon

0

这是对这个问题的接受答案 使用Entity Framework的强类型ASP.NET MVC

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

你试过那个了吗?

[更新,顶部的代码不起作用]

这是我使用的小扩展属性,以便下一个代码块更容易理解:

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;
        }
    }
}

这对我有用(这次肯定没问题):

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();

肯定看起来很hacky。我认为原因是因为EF关系具有实体状态(修改、添加、删除),基于该状态,EF更改外键的值或在多对多关系的情况下删除行。由于某种原因(不知道为什么),关系状态与属性状态不同。这就是为什么我不得不在originalItem上设置CategoryReference.EntityKey,以便更改关系的状态。


当我尝试使用那种方法时,什么也没有被保存。 - Echilon
抱歉Echilion,看看更新后的答案。这次肯定可以工作了。 - Misha N.
终于!谢谢Misha,我差点放弃EF了,但你恢复了我的信心! - Echilon
EF 似乎有些过度设计了。 - anar khalilov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接