UpdateModel not updating the model via ViewModel and property from DropDownListFor

Zaak picture Zaak · May 1, 2011 · Viewed 8.1k times · Source

I am trying to set up an Edit view on which I have a text box and DropDownListFor. I have figured out a way to populate the DDLF, and the rendered and posted values are correct, but i cant seem to get the model to update properly.

The object i am trying to update is generated from LINQtoSQL, and in database it has foreign key column. In LINQtoSQL class that resulted in "Contains" relationship. I can get to ID property that represents the column in DB, and also the object that it represents.

        zupanija = new Zupanija();       //object that needs to be updated
        zupanija.Drzava;                 //object that i want to change to make the update
        zupanija.DrzavaID;               //Property linked to object that should change

Only way i have figured out to do the update is to get the value from DDLF and use it to get the object that i want to change like this:

        [HttpPost]
        public ActionResult Edit(int id, FormCollection collection)
        {
         var zupanija = repo.ZupanijaById(id);
         var drzava = new repoDrzava().DrzavaById(Convert.ToInt32(collection["Zupanija.DrzavaID"]));
         zupanija.Drzava = drzava;
        }

Also when i try to update the ID field like this, then i get the folowing error:

           zupanija.DrzavaID = Convert.ToInt32(collection["Zupanija.DrzavaID"]);

Error: throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();

This seems to me that it is very lousy way to do this, and i am trying to get UpdateModel to work.

Answer

Zaak picture Zaak · May 2, 2011

I have found the solution while looking for something else, in blog by Joe Stevens:

Using Controller UpdateModel when using ViewModel

The catch is in following: When view model is used then to correctly bind the properties it is necessary to "instruct" the UpdateModel helper how to find the actual class we wish to update.

My solution required to modify

 UpdateModel(zupanija); to  UpdateModel(zupanija,"Zupanija");

Because i was using a ViewModel class that contained couple properties along with the main data class i wanted to update. Here is the code, i hope it helps to understand:

    public class ZupanijaFVM
    {
    public IEnumerable<SelectListItem> Drzave { get; private set; }
    public Zupanija Zupanija { get; private set; }
    ...
    }

    // From Controller
    //
    // GET: /Admin/Zupanije/Edit/5
    public ActionResult Edit(int id)
    {
        var zupanija = repo.ZupanijaById(id);
        return zupanija == null ? View("Error") : View(new ZupanijaFVM(repo.ZupanijaById(id)));
    }

    //
    // POST: /Admin/Zupanije/Edit/5

    [HttpPost]
    public ActionResult Edit(int id, FormCollection collection)
    {
        var zupanija = repo.ZupanijaById(id);
        if (TryUpdateModel(zupanija, "Zupanija"))
        {
            repo.Save();
            return RedirectToAction("Details", new { id = zupanija.ZupanijaID });
        }
        return View(new ZupanijaFVM(zupanija));
    }

     //From View:
     @model VozniRed.Areas.Admin.Models.ZupanijeFVM

     <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
     <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
      @using (Html.BeginForm()) 
      {
        @Html.ValidationSummary(true)
        <fieldset>
         <legend>Zupanija</legend>
          @Html.HiddenFor(model => model.Zupanija.ZupanijaID)
         <div class="editor-label">
          @Html.LabelFor(model => model.Zupanija.Naziv)
         </div>
         <div class="editor-field">
          @Html.EditorFor(model => model.Zupanija.Naziv)
          @Html.ValidationMessageFor(model => model.Zupanija.Naziv)
         </div>
         <div class="editor-label">
          @Html.LabelFor(model => model.Zupanija.Drzava)
         </div>
         <div class="editor-field">
          @Html.DropDownListFor(model => model.Zupanija.DrzavaID, Model.Drzave)
          @Html.ValidationMessageFor(model => model.Zupanija.DrzavaID)
         </div>
         <p>
          <input type="submit" value="Save" />
         </p>
       </fieldset>
       }
    <div>
     @Html.ActionLink("Back to List", "Index")
    </div>