[MVC3] ViewModel Binding To Controller–POST Methods


Now we should be writing strongly typed Views based on our Models. Now in some cases you may want to use the ViewModel pattern, which allows you to create a View based on:

 

a) One Model and some additional properties

b) Two or more combined Models

c) Two or more combined Models plus some additional properties.

These ViewModel classes would sit in a folder structure, you guessed it, ViewModels in your Visual Studio Project.An example would be my RegisterViewModel i have created:

 

 

public class RegisterViewModel {
    public RegisterModel RegisterModel { get; set; }
    public string UniqueKey { get; set; }
    public bool Thumbnail { get; set; }

}

 

 

So this is quite straight forward really. I have a RegisterModel which is the MVC default Membership model for handling when a new user Registers. I have a property called UniqueKey which will hold the of a created user, and a Boolean field Thumbnail which will be set to true if he want thumbnail images or false if we don’t.

So this is nice a simple then. So what this means that in our GET Action controller for register we can setup the required values. In this instance we only set the Thumbnail property:

 

// GET: /Account/Register public ActionResult Register()
{
    var viewModel = new RegisterViewModel {
        Thumbnail = true };

    return View(viewModel);
}

 

 

Then in our Review View, we need to declare our ViewModel instead of our RegisterModel, plus all our Model fields and the additional properties are parsed:

 

@model MvcImage.ViewModels.RegisterViewModel @using MvcImage.Models
@using Bradaz.OpenSource.MVC
@using Bradaz.OpenSource.MVC.HTMLHelpers

@{     ViewBag.Title = "Register";
} @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.")
    <div> <fieldset> <legend>Account Information</legend> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.UserName)
            </div> <div class="editor-field"> @Html.TextBoxFor(model => model.RegisterModel.UserName)
                @Html.ValidationMessageFor(model => model.RegisterModel.UserName)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.Email)
            </div> <div class="editor-field"> @Html.TextBoxFor(model => model.RegisterModel.Email)
                @Html.ValidationMessageFor(model => model.RegisterModel.Email)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.Password)
            </div> <div class="editor-field"> @Html.PasswordFor(model => model.RegisterModel.Password)
                @Html.ValidationMessageFor(model => model.RegisterModel.Password)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.ConfirmPassword)
            </div> <div class="editor-field"> @Html.PasswordFor(model => model.RegisterModel.ConfirmPassword)
                @Html.ValidationMessageFor(model => model.RegisterModel.ConfirmPassword)
            </div> <div class="editor-label"> @Html.Label("Avatar")
            </div> <div class="editor-field"> @Html.UploadImageFor(model => model.UniqueKey,thumbnail:true)
            </div> <div class="editor-field"> @Html.HiddenFor(model => model.Thumbnail)
            </div> <p> <input type="submit" value="Register" /> </p> </fieldset> </div> }

 

NB: I have changed my UploadImageFor HTML Helper to include Thumbnail support. This will be documented separately and released under my Open Source MvcImage Project.

So this all sounds simple right? Well you may get into a bit of a pickle (like me) when handling the POST Register ActionMethod. This would be peoples first attempts, which are both wrong:

Attempt 1

[HttpPost]
public ActionResult Register(RegisterModel registermodel,
    HttpPostedFileBase imageLoad2)
{
    ///---Code something }

Attempt 2

[HttpPost]
public ActionResult Register(RegisterViewModel registerviewmodel,
    HttpPostedFileBase imageLoad2)
{
    ///---Code something }

 

These are wrong if you wish to access the additional properties and the ResgiterModel within the RegisterViewModel. Let me explain:

 

Attempt 1 – You would be able to access the RegisterModel properties but not the Thumbnail property.

 

Attempt 2 – Your RegisterModel would be null and you wont be able to access the Thumbnail.

 

After these attempts most people scratch their head, curse, then head over to ASP.NET/FORUMS or stackoverflow for an answer. Well it took me a lot of scratching, googling and forum posting (plus nagging my poor friend Shawson!) to find the answers. The answer lies in how MVC binds the Models. The best place to start is in Fiddler, which is a free application for debugging HTTP and is very useful. Here is a glimpse of the form POST when i hit the Register button on my view:

 

 

Capture_Blog

 

As you can see our RegisterModel properties are prefixed with “RegisterModel” and our Thumbnail is just Thumbnail. So if you apply the MVC Binding on this, in the two attempts above, in the first attempt, MVC recognises RegisterModel as these have been binded from the HTTP post data, so are available. Thumbnail though isn’t available in the RegisterModel declaration in the class so this is ignored. In the 2nd attempt there is no RegisterViewModel in the POST so the binding will fail, thus nothing is available and you will get null fields.

 

The answer is the following:

[HttpPost]
public ActionResult Register(bool Thumbnail, 
[Bind(Prefix = "RegisterModel")]RegisterModel registermodel,
    HttpPostedFileBase imageLoad2)
{
    ///-Code something }

 

So here we declare Thumbnail as its own separate parameter and Bind the RegisterModel with a Prefix “RegisterModel” using the BindAttribute.  This allows us toaccess our RegisterViewModel properties AND Models within our controller quite happily.

I hope this helps you when using ViewModels.

Enhanced by Zemanta

[MVC 3] Entity Framework and Serializing JSON–Circular References


ADO.NET Entity Framework stack

Image via Wikipedia

I’m at the moment trying to write a light-weight AJAX control for my home page, which displays New Properties which have been added, aptly named WhatsNew.

The issue i had was the following error which i inspected in Fiddler when i didn’t have any JSON returned to my view:

A circular reference was detected while serializing an object type of “System.Data.Metadata.Edm.Properties”

A bit of googling, and it seems the JavaScriptSerializer has gets it knickers in a twist trying to traverse your Entity Framework objects which have relationships.

There are many options, but the one i took was to make the JSON as lightweight as possible and pass a ViewModel with only and not Entity Framework at objects at all. I build the view model based on data from the Entity Framework.

Here is my viewModel – the List was my old implementation which was causing the problems.

This will be returned as a JSON ActionResult to my view.  Bingo – I’ve fixed the issue! Smile

public class WhatsNewViewModel {
    //public List<PropertyViewModel> Properties { get; set; } 
 public string UniqiueKey { get; set; }
    public string Area { get; set; }
    public string PropertyType { get; set; }
}

Enhanced by Zemanta