【问题标题】:InvalidOperationException when using updatemodel with EF4.3.1将 updatemodel 与 EF4.3.1 一起使用时出现 InvalidOperationException
【发布时间】:2012-07-14 19:55:00
【问题描述】:

当我更新我的模型时,我在子关系上遇到错误,我也尝试更新。

我的模型,说 Order 与 OrderItem 有关系。在我看来,我有订单的详细信息以及订单项的编辑器模板。当我更新数据时,指向 Order 的链接为空,但 orderid 已填充,因此它应该能够链接它,TryUpdateModel 返回 true,但保存失败:

InvalidOperationException:操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。]

我的更新方法:

    public ActionResult ChangeOrder(Order model)
    {
        var order = this.orderRepository.GetOrder(model.OrderId);

        if (ModelState.IsValid)
        {
            var success = this.TryUpdateModel(order);
        }

        this.orderRepository.Save();

        return this.View(order);
    }

我尝试了我在 SO 和其他来源上看到的所有解决方案,但都没有成功。

我将 .Net MVC 3、EF 4.3.1 与 DBContext 一起使用。

【问题讨论】:

  • 一般不要使用TryUpdateModel,因为它在黑匣子中太多。我通常喜欢手动设置值。尝试在不使用该辅助方法的情况下手动执行操作。
  • 您可能需要类似以下的解决方案:stackoverflow.com/a/5540956/270591(使用子集合更新分离的父级)。 TryUpdateModel 不会做所有必要的工作来执行正确的更新。
  • 能否提供Order的代码?

标签: asp.net-mvc asp.net-mvc-3 entity-framework


【解决方案1】:

这里有很多代码异味,我会在更正时尽量优雅:)

我只能假设“订单”是您的 EF 实体?如果是这样,我强烈建议通过为表单创建视图模型并将数据复制到其中来将其与视图分开。您的视图模型实际上应该只包含您的表单将使用或操作的属性。

我还假设 orderRepository.GetOrder() 是从数据存储中检索订单的数据层调用?

您还声明了可能未使用的变量。即使您的模型无效,也会加载“var order =”,并且永远不会使用“var success =”。

TryUpdateModel 和 UpdateModel 对于实际编程来说不是很健壮。老实说,我并不完全相信他们应该在那里。我一般使用更抽象的方式,比如服务/工厂模式。它需要更多的工作,但给你更多的控制权。

在您的情况下,我会推荐以下模式。有最小的抽象,但它仍然比使用 TryUpdateModel / UpdateModel 为您提供更多控制:

    public ActionResult ChangeOrder(OrderViewModel model) {
        if(ModelState.IsValid) {
            // Retrieve original order
            var order = orderRepository.GetOrder(model.OrderId);

            // Update primitive properties
            order.Property1 = model.Property1;
            order.Property2 = model.Property2;
            order.Property3 = model.Property3;
            order.Property4 = model.Property4;

            // Update collections manually
            order.Collection1 = model.Collection1.Select(x => new Collection1Item {
                Prop1 = x.Prop1,
                Prop2 = x.Prop2
            });

            try {
                // Save to repository
                orderRepository.SaveOrder(order);
            } catch (Exception ex) {
                ModelState.AddModelError("", ex.Message);
                return View(model);
            }
            return RedirectToAction("SuccessAction");
        }
        return View(model);
    }

不理想,但它应该为您提供更好的服务...

我把你推荐给this 的帖子,类似的。

【讨论】:

    【解决方案2】:

    我假设用户可以在您的视图中执行以下操作:

    1. 修改订单(标题)数据
    2. 删除现有订单项
    3. 修改订单商品数据
    4. 添加新订单项

    要正确更新已更改的对象图(订单 + 订单项列表),您需要处理所有四种情况。 TryUpdateModel 将无法正确更新数据库中的对象图。

    我直接使用context 编写以下代码。您可以将上下文的使用抽象到您的存储库中。确保在以下代码中涉及的每个存储库中使用相同的上下文实例。

    public ActionResult ChangeOrder(Order model)
    {
        if (ModelState.IsValid)
        {
            // load the order from DB INCLUDING the current order items in the DB
            var orderInDB = context.Orders.Include(o => o.OrderItems)
                .Single(o => o.OrderId == model.OrderId);
    
            // (1) Update modified order header properties
            context.Entry(orderInDB).CurrentValues.SetValues(model);
    
            // (2) Delete the order items from the DB
            // that have been removed in the view
            foreach (var item in orderInDB.OrderItems.ToList())
            {
                if (!model.OrderItems.Any(oi => oi.OrderItemId == item.OrderItemId))
                    context.OrderItems.Remove(item);
                    // Omitting this call "Remove from context/DB" causes
                    // the exception you are having
            }
    
            foreach (var item in model.OrderItems)
            { 
                var orderItem = orderInDB.OrderItems
                    .SingleOrDefault(oi => oi.OrderItemId == item.OrderItemId);
    
                if (orderItem != null)
                {
                    // (3) Existing order item: Update modified item properties
                    context.Entry(orderItem).CurrentValues.SetValues(item);
                }
                else
                {
                    // (4) New order item: Add it
                    orderInDB.OrderItems.Add(item);
                }
            }
    
            context.SaveChanges();
    
            return RedirectToAction("Index"); // or some other view
        }
    
        return View(model);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-10-19
      • 1970-01-01
      • 1970-01-01
      • 2021-02-28
      • 2017-12-04
      • 2022-01-04
      • 2017-05-09
      • 1970-01-01
      • 2023-03-29
      相关资源
      最近更新 更多