Monday, February 28, 2011

MVC renders wrong value?

I had a situation where MVC appeared to be reverting a change made in code to property of my view model inside the controller action.

Let's get the picture. My model is a healthcare Practitioner. Here's a simplified version of the code (extraneous properties removed):
public class Practitioner
{
    [Required]
    public string ObjectIdValue { get; set; }

    public string VersionUidValue { get; set; }

    [Required]
    public string FacilityUid { get; set; }

    [Required]
    public string FamilyName { get; set; }

    [Required]
    public string GivenName { get; set; }
}


The action to post an updated Practitioner is called Edit and it looks a bit like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Practitioner editPractitioner)
{
    if (ModelState.IsValid)
    {
        // ... do some checking ...

        this.practitionerProvider.Edit(editPractitioner);
        this.practitionerProvider.Commit();

        // Ensure VersionUid up to date
        Practitioner updated = this.practitionerProvider.Get(editPractitioner.ObjectId());
        editPractitioner.VersionUidValue = updated.VersionUidValue;

        this.notifier.Information("Practitioner saved.");
    }
    else
        NotifyErrors();

    return View(editPractitioner);
}


Now, ignoring the fact that this might be an inefficient way to ensure the VersionedUidValue is up to date, there were some strange results when the resulting model was rendered by the view. Here's some detail from the view markup:
<div>
<% using (Html.BeginForm("Edit", "Practitioner", FormMethod.Post))
    { %>

    <label>Family name</label> <%= Html.TextBoxFor(m => m.FamilyName) %>
    <label>Given name</label> <%= Html.TextBoxFor(m => m.GivenName) %>

    <%= Html.HiddenFor(m => m.FacilityUid) %>
    <%= Html.HiddenFor(m => m.ObjectIdValue) %>
    <%= Html.HiddenFor(m => m.VersionUidValue) %>

    <p>(DEBUG) Current Version UID Value is: <%=Model.VersionUidValue %></p>

    <a href="#" id="back">Cancel</a>
    <input type="submit" value="Save" class="submit" />

<% } %>
</div>


Note that the VersionUidValue property is being used inside a hidden field as well as being printed out for debugging purposes. But when this model was posted back to the same action, the VersionUidValue was not up to date, but rather the string that had been previously posted (one whole postback ago) to the controller.

This was confusing until I checked the HTML rendering using Fiddler (fiddler2.com) and it looked like this:

<input id="VersionUidValue" name="VersionUidValue" type="hidden" value="e1ae76cc-c443-4ad9-bd43-6cc3105b965d::integration.test.demographics::18" />

<p>Current Version UID Value is: e1ae76cc-c443-4ad9-bd43-6cc3105b965d::integration.test.demographics::19</p>


Note the different endings to those two strings.

This was bizarre and inexplicable behaviour until I found out just how much MVC helpers are really trying to help.

Simon Ince's blog post explains why and I won't try improve on his explanation: ASP.NET MVC’s Html Helpers Render the Wrong Value!

So it turns out the best-practice solution is to do a redirect to a confirmation (which might then redirect back to the Edit screen) in order to ensure the old crap in the ModelState is not something being observed by MVC during rendering.

No comments:

Post a Comment