0

I'm having a little issue with an Edit view on a ViewModel. When I post my Edit view to the server for the first time, it needs to return the Edit view again with the same ViewModel where the database ID has been appended to.

This is the Edit method in the appropriate controller:

[HttpPost]
public ActionResult Edit(InvoiceDetailsViewModel invoice) {
  using (var context = new HyperContext(WebSecurity.CurrentUserId)) {
    if (ModelState.IsValid) {
      if (invoice.ID == 0) {
        var dbItem = Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice);
        context.Invoices.Add(dbItem);
        context.SaveChanges();
        var newInvoice = Mapper.Map<InvoiceDetailsViewModel>(dbItem);
        FillViewBag(context, newInvoice);
        newInvoice.Description = "TEST";
        return PartialView(newInvoice);
      }
      else {
        context.Entry(Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice)).State = System.Data.EntityState.Modified;
        context.SaveChanges();
        return Content(Boolean.TrueString);
      }
    }
    FillViewBag(context, invoice);
    return PartialView(invoice);
  }
}

The relevant part here is where the invoice.ID is 0, the invoice is saved to the DB to get an ID and returned to the Edit view. In that view I got these lines for starters:

@model eu.ecmt.RecruitmentDatabase.ViewModels.InvoiceDetailsViewModel

@using (Html.BeginForm("Edit", "Invoice", FormMethod.Post, new { id = "invoices-edit-form" })) {
    @Html.ValidationSummary(true)

    <script type="text/javascript">
        $(document).ready(function () {
            //$("#tabs").tabs();
            InitProfileUI();
        });
    </script>
    if (Model.ID != 0) {
        <script type="text/javascript">
            $(document).ready(function () {
                LoadList('/InvoiceDetail/List/@Model.ID', '', 'invoice-details');
            });
        </script>
    }
    <fieldset>
        <legend>Edit contract</legend>

        @Html.HiddenFor(m => m.ID)
        @Html.HiddenFor(m => m.InvoiceNumber)
        @Html.HiddenFor(m => m.Created)
        @Html.HiddenFor(m => m.CreatedBy)
        @Html.HiddenFor(m => m.Modified)
        @Html.HiddenFor(m => m.ModifiedBy)

When first rendering this view, the script element containing the LoadList call is not in the output. When the form is posted and the view is rendered with the updated viewmodel, that element is in the output. The hidden field containing the ID of the invoice, though, still shows 0. So, in essence, what is happening here, is that the Model object in the ViewData dictionary is the correct version, the object that is being used in the expressions seems to be another, older, version.

Anyone care to explain this and point me into the right direction?

Sylvain Girard
  • 368
  • 3
  • 13
  • Where do you call ViewData model in this example? Did you check that var newInvoice has ID different then 0 in the controller? – Nenad Feb 22 '13 at 02:07
  • Model.ID call the model in the viewdata. I have checked everything in with the debugger and all is well. The only thing I cannot check, as far as I know, are the lambda expressions, which are not correct. – Sylvain Girard Feb 22 '13 at 02:42
  • Model.ID and m => m.ID use same model, one you passed into PartialView(...) call (which is internally passed via ViewData). Why storing same model in ViewBag manually with FillViewBag? – Nenad Feb 22 '13 at 02:54
  • FillViewBag doesn't do what it's name suggests :) It merely sets a property in the viewmodel and is up for refactoring. I find it hard to believe that Model.ID and m => m.ID use the same object as the result would be what I expect it to be. At the moment Model is the new version and m is the old version, so to say... – Sylvain Girard Feb 22 '13 at 07:56

1 Answers1

1

It seems to be behavior by design, according to this post.

Summary: the HTMLHelper will first use the values in the POST, then it will use the values from the actual model. Removing them from ModelState in the controller method did the trick:

[HttpPost]
public ActionResult Edit(InvoiceDetailsViewModel invoice) {
  using (var context = new HyperContext(WebSecurity.CurrentUserId)) {
    if (ModelState.IsValid) {
      if (invoice.ID == 0) {
        ModelState.Remove("ID");
        ModelState.Remove("Created");
        ModelState.Remove("CreatedBy");
        ModelState.Remove("Modified");
        ModelState.Remove("ModifiedBy");
        var dbItem = Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice);
        context.Invoices.Add(dbItem);
        context.SaveChanges();
        invoice = Mapper.Map<InvoiceDetailsViewModel>(dbItem);
        FillViewBag(context, invoice);
        return PartialView(invoice);
      }
      else {
        context.Entry(Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice)).State = System.Data.EntityState.Modified;
        context.SaveChanges();
        return Content(Boolean.TrueString);
      }
    }
    FillViewBag(context, invoice);
    return PartialView(invoice);
  }
}
Community
  • 1
  • 1
Sylvain Girard
  • 368
  • 3
  • 13