如何在MVC 5和EF 6中保存对相关表的更改 - 实体框架主 - 详细信息

时间:2015-11-03 18:56:19

标签: c# asp.net-mvc entity-framework

我有两个表,Promotion和PromotionLine,外键定义为PromotionLine.PromoID = Promotion.ID

PromotionLines与Promotion模型相关联,Promotion类中包含以下内容:

public IList<PromotionLine> PromotionLineItems { get; set; }

我选择不使用虚拟广告,因为如果我们只是使用摘要视图来显示高级促销信息(例如促销列表),我就不想加载宣传线。

当需要推广详情时,我会获得推广热线:

    public static Promotion GetInstance(int? promotionId)
    {
        if (promotionId == null) return null;
        using (APP01Entities entities = new APP01Entities())
        {
            return entities.Promotions
                .Include(s => s.PromotionState)
                .Include(h => h.PromotionHeaderType)
                .Include(l => l.PromotionLineItems)
                .Include(c => c.PromotionComments)
                .FirstOrDefault(p => p.ID == promotionId);
        }

    }

这很有效,我可以在我的视图中访问促销行。

但是,当我去更新更改时,我遇到错误:

  

“发生了参照完整性约束违规:属性   &。促销活动的价值&#39;在一段关系的一端不匹配   &lt; PromotionLine.PromotionID&#39;的属性值在另一   端“。

我理解为什么会出现这种错误。我不知道如何绕过它。 我使用默认更新方法(由EF脚手架创建):

public bool Update()
    {
        try
        {
            using (APP01Entities entities = new APP01Entities())
            {
                entities.Promotions.Attach(this);

                var entity = entities.ChangeTracker.Entries<Promotion>().FirstOrDefault(e => e.Entity == this);

                if (entity == null)
                {
                    return false;
                }
                entity.State = EntityState.Modified;
                entities.SaveChanges();
            }
            return true;
        }
        catch (System.Data.Entity.Validation.DbEntityValidationException e)
        {
            throw e.Improve();
        }
    }

问题在于:

entities.Promotions.Attach(this);

&#34;这&#34;有促销线。 entities.Promotions没有。

以下是我调用更新方法的方法:

    [HttpPost]
    public ActionResult Edit(Promotion promotion)
    {
        if (ModelState.IsValid)
        {
            promotion.Update();
        }
        return View(promotion);
    }

问题

  • 如何将促销行添加到entities.Promotions?
  • 或者,我应该以不同的方式接近此更新吗?

1 个答案:

答案 0 :(得分:2)

这不是一项微不足道的任务。您需要更新对象图。换句话说,您需要更新Master-Detail。

为了使答案简单,我想我的Order实体的OrderDetails属性为List<OrderDetail>。编辑时应注意:

  • 有一些OrderDetail已添加到OrderDetails,其主要标志是Id属性等于0.
  • 某些OrderDetail已被移除,OrderDetails
  • 中已不存在
  • 某些OrderDetail已更改。

更新Order时,您应该更新Order本身并应用上述更改。

<强>步骤

以下是步骤:

  1. 从数据库获取原始订单。
  2. 使用已编辑的订单更新原始订单的值。
  3. 查找已添加项目的列表(已添加项目的ID为0)。
  4. 查找已删除项目的列表(原始订单的订单详细信息列表,其中原始订单详细信息的ID不在编辑订单的订单详细信息的ID之间)。
  5. 查找已编辑项目的列表(原始订单的订单详细信息列表,其中原始订单详细信息的ID在编辑订单的订单详细信息的ID之间)。
  6. 对已删除的项目列表使用循环,并将其状态设置为已删除。
  7. 在已编辑的项目列表上使用循环,并更新已在上下文中加载的原始订单详细信息的值。
  8. 对添加的项目列表使用循环,并将其状态设置为已添加。
  9. 将原始订单的状态设置为已修改。
  10. 保存上下文更改。
  11. <强>代码

    以下是代码:

    public void Update(Order editedOrder)
    {
        var context = new YourDbContext();
    
        //Get original order from database.
        var originalOrder = context.Orders.Including("OrderDetails").Where(x => x.OrderId == editedOrder.OrderId).FirstOrDefault();
    
        //Update the value of original order using edited order.
        context.Entry(originalOrder).CurrentValues.SetValues(editedOrder);
    
        //Find list of added items (Id of added items is 0).
        var addedList = editedOrder
            .OrderDetails
            .Where(y => y.OrderDetailId == 0)
            .ToList();
    
        //Find list of removed items.
        var deletedList = originalOrder
            .OrderDetails
            .Where
            (
                x =>
                (
                    !editedOrder.OrderDetails
                    .Select(y => y.OrderDetailId)
                    .Contains(x.OrderDetailId)
                )
            )
            .ToList();
    
        //Find list of edited items.
        var editedList = editedOrder.OrderDetails
            .Where
            (
                y => originalOrder
                .OrderDetails
                .Select(z => z.OrderDetailId)
                .Contains(y.OrderDetailId)
            )
            .ToList();
    
        //Use a loop over deleted items list and set state of them to removed.
        deletedList.ForEach(deletedDetail =>
        {
            originalOrder.OrderDetails.Remove(deletedDetail);
            context.Entry(editedOrder).State = EntityState.Deleted;
        });
    
        //Use a loop over edited items list and update value of original order details  that have been loaded in context.
        editedList.ForEach(editedDetail =>
        {
            var originalOrderDetail = originalOrder.OrderDetails.Where(x => x.OrderDetailId == editedDetail.OrderDetailId).FirstOrDefault();
           context.Entry(originalOrderDetail).CurrentValues.SetValues(editedDetail);
        });
    
        //Use a loop over added items list and set state of them to added.
        addedList.ForEach(addedDetail =>
        {
            originalOrder.OrderDetails.Add(addedDetail);
        });
    
        //Set the state of original order to modified.
        context.Entry(oroginalOrder).State = System.Data.Entity.EntityState.Modified;
    
        //Save context changes.
        context.SaveChanges();
    }