InvalidOperationException when using updatemodel with EF4.3.1

When I update my model, I get a child relationship error message that I am also trying to update.

My model, say, the Order has a release with OrderItem. In my opinion, I have order details along with editortemplate for orderitems. When I update the data, the link to "Order" is NULL, but the customer is filled, so he should be able to bind it, TryUpdateModel returns true, but the failure persists:

InvalidOperationException: operation failed: the relation cannot be changed because one or more properties of the foreign key cannot be nullified. When a change in relationship occurs, the corresponding property of the foreign key is set to zero. If the foreign key does not support null values, a new relationship must be defined, another nonzero value must be assigned to the foreign key property, or an object not associated with it must be deleted.]

My update method:

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

I tried all the solutions that I saw on SO and other sources, nobody succeeded.

I am using .Net MVC 3, EF 4.3.1 along with DBContext.

+6
source share
2 answers

There are a number of code smells that I will try to fix :)

I can only assume that the "Order" is your object EF? If so, I highly recommend keeping it separate from the submission by creating a submission model for your form and copying the data into it. Your view model should only contain properties that your form will use or manipulate.

I also assume orderRepository.GetOrder () is a data layer call that retrieves an order from a data store?

You also declare potentially unused variables. " var order = " will be loaded even if your model is invalid and " var success = " is never used.

TryUpdateModel and UpdateModel are not very reliable for real programming. I'm not quite sure that they should be there at all, if I'm honest. I usually use a more abstract approach, such as the / factory service pattern. This works more, but gives you much more control.

In your case, I would recommend the following template. There's a minimal abstraction, but it still gives you more control than using 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); } 

Not perfect, but it should serve you a little better ...

I am referring you to this post, which seems to be.

+4
source

I assume that the user can perform the following actions in your view:

  • Change order (header) data
  • Delete existing order item
  • Change order item data
  • Add new order item

In order to correctly update the changed graph of objects (order + list of order items), you need to deal with all four cases. TryUpdateModel will not be able to correctly update the graph of objects in the database.

I am writing the following code directly using context . You can abstract the use of context in your repository. Make sure that you use the same context instance in each repository that is involved in the following code.

 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); } 
+3
source

Source: https://habr.com/ru/post/920500/


All Articles