8

I have a table in my database with a one to many relationship to another table, which has a relationship to a third table:

ParentObject

  • ID
  • Name
  • Description

ChildObject

  • ID
  • Name
  • Description
  • ParentObjectID
  • AnotherObjectID

AnotherObject

  • ID
  • Name

The objects are mapped into Entity Framework and exposed through a data access class.

It seemed like ViewModels are recommended when the data to be displayed greatly differs from the domain object, so I created a ViewModel as follows:

public class ViewModel {
    public IList<ParentObject> ParentObjects { get; set; }
    public ParentObject selectedObject { get; set; }
    public IList<ChildObject> ChildObjects { get; set; }
}

I have a view that displays a list of ParentObjects and when clicked will allow a ChildObject to be modified saved.

<% using (Html.BeginForm()) { %> 
<table>
    <% foreach (var parent in Model.ParentObjects) { %>
    <tr>
        <td>
            ObjectID [<%= Html.Encode(parent.ID)%>]
        </td>
        <td>
            <%= Html.Encode(parent.Name)%>
        </td>
        <td>
            <%= Html.Encode(parent.Description)%>
        </td>
    </tr>
    <% } %>
</table>
<% if (Model.ParentObject != null) { %>
<div>
    Name:<br />
    <%= Html.TextBoxFor(model => model.ParentObject.Name) %>
    <%= Html.ValidationMessageFor(model => model.ParentObject.Name, "*")%>
</div>
<div>
    Description:<br />
    <%= Html.TextBoxFor(model => model.ParentObject.Description) %>
    <%= Html.ValidationMessageFor(model => model.ParentObject.Description, "*")%>
</div>
<div>
    Child Objects
</div>
<% for (int i = 0; i < Model.ParentObject.ChildObjects.Count(); i++) { %>
    <div>
        <%= Html.DisplayTextFor(sd => sd.ChildObjects[i].Name) %>
    </div>
    <div>
        <%= Html.HiddenFor(sd => sd.ChildObjects[i].ID )%>
        <%= Html.TextBoxFor( sd => sd.ChildObjects[i].Description) %>
        <%= Html.ValidationMessageFor(sd => sd.ChildObjects[i].Description, "*") %>
    </div>
    <% }
}
} %>  

This all works fine. My question is around the best way to update the EF objects and persist the changes back to the database. I initially tried:

[HttpPost]
    public ActionResult Edit(ViewModel viewModel) {
        ParentObject parent = myRepository.GetParentObjectByID(viewModel.SelectedObject.ID);

        if ((!ModelState.IsValid)
            || !TryUpdateModel(parent, "SelectedObject", new[] { "Name", "Description" })) {
            || !TryUpdateModel(parent.ChildObjects, "ChildObjects", new[] { "Name", "Description" })) {


            //Code to handle failure and return the current model snipped

            return View(viewModel);
        }

        myRepository.Save();

        return RedirectToAction("Edit");
    }

When I try to save a change to the child object, I get this exception: Entities in 'MyEntities.ChildObject' participate in the 'FK_ChildObject_AnotherObject' relationship. 0 related 'AnotherObject' were found. 1 'AnotherObject' is expected.

Investigation on StackOverflow and general googling led me to this blog post that seems to describe my problem: TryUpdateModel() does not correctly handle nested collections. Apparently, (and stepping through the debugger confirms this) it creates a new ChildObject instead of associating with the EF objects from my instantiated context.

My hacky work around is this:

if (viewModel.ChildObjects.Count > 0) {
    foreach (ChildObject modelChildObject in viewModel.ChildObjects) {
        ChildObject childToUpdate = ParentObject.ChildObject.Where(a => a.ID == modelChildObject.ID).First();
        childToUpdate.Name = modelChildObject.Name;
    }
}

This seems to work fine. My question to you good folks: Is there a correct way to do this? I tried following the suggestion for making a custom model binder per the blog link I posted above but it didn't work (there was an issue with reflection, the code expected certain properties to exist) and I needed to get something going ASAP.

PS - I tried to cleanup the code to hide specific information, so beware I may have hosed something up. I mainly just want to know if other people have solved this problem.

2
  • Sorry for bringing this back up, but I'm having a similar problem.. Can't figure out how to keep all my data access in the same "context" between view, controller and repository.. So I get duplicates in my db.. Did you find a "correct way" of doing all this? Thanks Commented Jun 27, 2011 at 11:58
  • We ended up basically working around the issue by not using TryUpdateModel in complex situations. Your issue sounds different though, you might look into detaching/attaching your entities from your contexts. Commented Jun 29, 2011 at 19:22

2 Answers 2

1

Just briefly looking at the error mentioning FK_ChildObject_AnotherObject... are you sure everything is wired up correctly in your EF data model concerning AnotherObject?

In your question you only list two tables, but this error is indicating that the ChildObject is participating in a 1 to * relationship with AnotherObject and there is not one present in the entity on save causing an error.

Try removing AnotherObject from the situation in order to test any assumed problems between ParentObject and ChildObject, or try changing the FK_ChildObject_AnotherObject relationship to 0..1 to * briefly for testing.

Sign up to request clarification or add additional context in comments.

2 Comments

I apologize for not included the third table in my brief overview. I have edited the original post to include the third table. Per your suggestion, I made the AnotherObject table 0..1. This changes the error to: A relationship is being added or deleted from an AssociationSet 'FK_ChildObject_ParentObject'. With cardinality constraints, a corresponding 'ChildObject' must also be added or deleted. This seems to imply that EF thinks I am trying to create another ChildObject... I even tried completely removing the AnotherObject relationship, but I still get the same error.
Hmmm, well the good news is that AnotherObject is out of the picture now and the focus is where it belongs, which is the relationship between Parent and Child. Are you still producing this error by adding/deleting a Child? This error seems to indicate you're working on the Parent primarily. Like, you added a Parent, but the Parent/Children associate is * to 1 instead of * to 0..1. Adding a Parent in that scenario would demand the existence of a child, and potentially produce an error similar to yours.
0

You could look at using Omu ValueInjector instead of tryupdate. It can be configured to be much smarter although I prefer to use it by convention.

tbh not entirely clear on the original question here. I'm not sure why the childobjects in the viewmodel are being detached form the parent in the first place?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.