Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InvalidOperationException when using updatemodel with EF4.3.1

When I update my model I get an error on a child relation which I also try to update.

My model, say Order has a releationship with OrderItem. In my view I have the details of the order together with an editortemplate for the orderitems. When I update the data the link to Order is null but the orderid is filled, so it should be able to link it, TryUpdateModel returns true, the save however fails with:

InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object 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 solutions I saw on SO and other sources, none succeeded.

I use .Net MVC 3, EF 4.3.1 together with DBContext.

like image 206
Bas Danen Avatar asked Jul 14 '12 19:07

Bas Danen


1 Answers

There are a number of code smells here, which I'll try to be elegant with when correcting :)

I can only assume that "Order" is your EF entity? If so, I would highly recommend keeping it separate from the view by creating a view model for your form and copying the data in to it. Your view model should really only contain properties that your form will be using or manipulating.

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

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

TryUpdateModel and UpdateModel aren't very robust for real-world programming. I'm not entirely convinced they should be there at all, if I'm honest. I generally use a more abstracted approach, such as the service / factory pattern. It's more work, but gives you a lot more control.

In your case, I would recommend the following pattern. There's 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 ideal, but it should serve you a bit better...

I refer you to this post, which is similar.

like image 81
Spikeh Avatar answered Oct 27 '22 15:10

Spikeh