ASP.NET MVC Updating a list of objects on one form? (model binding to a list)

10,690

One thing i noticed is that your not rendering <input type="hidden" name='results.Index' value='<%= i %>' /> as phil Haacks article mentions is mandatory.

Switching to a different Modelbinder might do the trick too. I use the DataAnnotations model binder and with that i dont have to generate .Index fields when binding to List's.

Share:
10,690

Related videos on Youtube

Sergio
Author by

Sergio

Updated on April 16, 2022

Comments

  • Sergio
    Sergio about 2 years

    Quick question regarding updating a list of items in asp.net mvc.

    Basically I have an edit action method that returns a collection of objects (incidentally, the table structure of which looks as follows 'testID, assetID, Result' - a link table).

    I basically want this items to be displayed one after another in a form and to be able to edit them. The form should post back and the modelbinder do its magic. But, its not that easy.

    I have scoured the net and it seems the majority of the information about this stuff seems to be a little out of date. I've come across this post, which has not been updated in a long time, and this one which seems to suggest that you shouldn't bind to a already existing list for updating, and that there are problems when working with EF or Linq to Sql (which I am).

    Is there an easy way to achieve what I want? Has the state of list model binding changed in the release version?

    UPDATE - A little closer...

    Here's my Edit method:

    public ActionResult EditSurveyResults(Guid id)
        {
            var results = surveyRepository.GetSurveyResults(id);
            return PartialView("EditSurveyResults", results);
        }
    

    And my form:

    <div id="editSurveyResults">
    <h2>
        EditSurveryResults</h2>
    
    <%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
    <% using (Html.BeginForm())
       {%>
    <fieldset>
        <legend>Results</legend>
        <% int i = 0; foreach (var result in Model)
       { %>
    
        <input type="hidden" name='results[<%= i %>].TestID' value='<%= result.TestID %>' />
        <input type="hidden" name='results[<%= i %>].AssetID' value='<%= result.AssetID %>' />
        <p>
            <%= result.Task.TaskName%>
        </p>
        <p>
            <label for="Result">
                Result:</label>
            <input type="text" name='results[<%= i %>].Result' value='<%= result.Result %>' />
            <%= Html.ValidationMessage("Result", "*")%>
        </p>
    
        <% i++; } %>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
    <% } %>
    

    And my Edit POST method:

     [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult EditSurveyResults(Guid id, IList<SurveyTestResult> results)
        {
            var oldValues = surveyRepository.GetSurveyResults(id);
    
            if (ModelState.IsValid)
            {
                UpdateModel(oldValues);
                surveyRepository.Save();
                return Content("Done");
            }
            else
                return PartialView("EditSurveyResults");
        }
    

    It's not complete of course, but it doesn't update anything in its current state. Am I missing a trick here? results is populated with the the updated entities so I'm not sure why its not updating...

    UPDATE 2: So, Im starting to think that the model binder cant do stuff like this. So, I've resorted to doing things in a more hacky way. If anyone can spot a problem with this then please let me know. FYI - this form will be grabbed with AJAX so I dont return a view, rather a simple message. Here's the new code:

    IList<SurveyTestResult> oldValues = surveyRepository.GetSurveyResults(id).ToList();
                foreach (var result in SurveyTestResult)
                {
                    //SurveyTestResult is the IList that comes down from the form.                    
                    SurveyTestResult thisone = oldValues.Single(p => p.AssetID == result.AssetID &&
                        p.TestID == result.TestID);
                    //update the old entity with the result from the new one
                    thisone.Result = result.Result;
                }
    

    And then I call Save on my repository.

    Thanks in advance