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

本文关键字:细节 框架 实体 MVC EF 保存 | 更新日期: 2023-09-27 18:20:22

我有两个表,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);
        }
    }

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

然而,当我去更新更改时,我遇到了错误:

"发生引用完整性约束冲突:属性关系一端的"Promotion.ID"的值不匹配另一个的"PromotionLine.PromotionID"的属性值结束。"

我理解为什么会出现这种错误。我不知道如何避开它。我使用默认的更新方法(由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);

"this"有促销语。实体。促销没有。

以下是我如何调用更新方法:

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

问题

  • 如何将促销线添加到实体中。促销
  • 或者,我应该以不同的方式处理此更新吗

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

这不是一项琐碎的任务。您需要更新对象图。换句话说,你需要一个主细节更新。

为了简单起见,我假设我有一个Order实体,它具有OrderDetails属性,即List<OrderDetail>。编辑时应注意:

  • 有一些OrderDetail被添加到OrderDetails中,它们的主符号是具有等于0的Id性质
  • 某些OrderDetail已被删除,不再存在于OrderDetails
  • 某些OrderDetail已更改

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

步骤

以下是步骤:

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

代码

这是代码:

public void Update(Order editedOrder)
{
    using(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();
    }
}