【问题标题】:Entity Framework Updating with Related Entity使用相关实体更新实体框架
【发布时间】:2009-10-23 10:47:50
【问题描述】:

我正在使用 EF 尝试使用 ASP.NET 更新实体。我正在创建一个实体,设置它的属性,然后将其传递回具有 ID 的单独层上的 EF,以便可以应用更改。我这样做是因为我只在实体绑定到 UI 控件时存储实体的 ID。

一切都适用于标准属性,但我无法更新产品(相关实体)的 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 中使用它时似乎遇到了一些问题。

【问题讨论】:

    标签: asp.net entity-framework foreign-keys


    【解决方案1】:

    失败的原因有两个。

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

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

    【讨论】:

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

    这是这个问题的公认答案Strongly-Typed ASP.NET MVC with Entity Framework

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

    肯定它看起来很老套。我认为原因是因为 EF 关系具有实体状态(修改、添加、删除),并且基于该状态,如果多对多关系发生,EF 会更改外键的值或删除行。由于某种原因(不知道为什么),关系状态没有像财产状态一样改变。这就是为什么我必须在 originalItem 上设置 CategoryReference.EntityKey 才能更改关系的状态。

    【讨论】:

    • 当我尝试该方法时没有保存任何内容。
    • 对那个 Echilion 感到抱歉,请查看更新后的答案。这一次肯定有效。
    • 终于!谢谢你 Misha,我正要放弃 EF 但你恢复了我的信心!
    • EF 似乎太过分了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 2011-12-10
    • 2019-04-14
    • 1970-01-01
    • 2012-03-22
    • 1970-01-01
    • 2014-10-23
    相关资源
    最近更新 更多