jb's Blog


"behold the turtle, he only makes progress when he sticks his neck out"

MVC and LightSpeed – Part 4 – Model Binding

tag icon Tagged as LightSpeed, MVC, jQuery

In our earlier instalment we started getting under way with our ASP.NET MVC site for the Film Festival and got our home page working by binding to our LightSpeed entities. Now we should look at what is involved with handling submission of a form where the target is a LightSpeed entity.

Setting up our Action

For this, I want to set up an admin form which we can use to create a new film. We wont concern ourselves too much with the UI here, but rather we are interested in how we can easily map our form data back to an entity and handle validation concerns.

Shot017

Here is the associated View action code:

    public ActionResult New()
    {
      var film = new Film() { Title = "New Film", Year = DateTime.Now.Year };
 
      return New(film);
    }
 
    [NonAction]
    public ActionResult New(Film film)
    {
      return View("New", film);
    }

Next I want to set up my Create() action which will take the POST from this form and will create me a new film.

There are several ways we could pass in input, either by named parameters, through a FormCollection object or through a Film instance directly – well that last option sounds ideal, particularly for when we come to write some tests, so how do I go about that? Surely the form knows nothing about my object structure, does MVC? How does this work?

Cue the Model Binder

If you have used another MVC framework such as Monorail, you will know that it provides some nice data binding support to allow you to map an inbound form collection to an object instance, or reconstruct a graph of objects. MVC provides us the same capabilities through Model Binders.

Model Binders are classes which provide the specifics of how to map a form collection (a bag of key/value pairs) back to a real entity. Out of the box there is a DefaultModelBinder available which does a blind property mapping.

I am going to create a specific model binder for LightSpeed entities which provide some specific understanding of how we want to perform this mapping.

The method we need to implement for IModelBinder is the BindModel method, in this call I am going to unbind all of the value fields on a LightSpeed entity based on the convention that the form parameter will map to a property name on my entity. If not, then we will ignore it. We could also possibly extend this by providing support for Included/Excluded properties which is something the DefaultModelBinder provides.

You can see the code for this class here: https://code.mindscape.co.nz/repos/LightSpeed/Mvc/Trunk/Src/Mindscape.LightSpeed.Mvc/Binding/EntityModelBinder.cs

To use this model binder, we will call EntityModelBinder.Register() from inside our Global.asax start up code – in turn this makes assignments to the ModelBinders.Binders collection.

Once these have been registered, the binding is handled automatically, as ASP.NET MVC sees a parameter and looks up if there is a specific model binder for that type, in the case of an entity there will be, and then it calls to BindModel() on that model binder to do the work.

So if we take this signature for our POST action -  public ActionResult Create(Film film) – then the result will be an instantiated Film entity.

Handling Validation

How do we determine if there are any errors with the inbound object? Well we can leverage LightSpeed’s baked in validation attributes to provide the answer.

When we first created the definition of our domain model, the LightSpeed Designer automatically assigned appropriate validations on the model based on the database definition – e.g.

  [Serializable]
  [System.CodeDom.Compiler.GeneratedCode("LightSpeedModelGenerator", "1.0.0.0")]
  public partial class Film : Entity
  {
    #region Fields
 
    [ValidatePresence]
    [ValidateLength(0, 255)]
    private string _title;
    [ValidatePresence]
    private string _description;

The ValidatePresence and ValidateLength attributes are specifying validation concerns.

In our model binder you will notice that after we have unbound the entity we call .IsValid – this triggers LightSpeed to validate the entity (it automatically does this when trying to save as well, so you would get an exception if you tried to save an invalid entity). We can then bind any errors it finds into the ModelState.

      if (!entity.IsValid)
      {
        foreach (var error in entity.Errors)
        {
          bindingContext.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
        }
      }

So applying this, and then pushing Create on my form (silly me) we get this

Shot018

Excellent! We can get the nice little validation summary by leveraging a method on the HtmlHelper class called ShowValidationSummary – I have set it up like this:

<!--Html.ValidationSummary("There were some errors with your submission..")-->

And my controller code now looks like this:

    public ActionResult Create(Film film)
    {
      if (!ModelState.IsValid)
      {
        return New(film);
      }
 
      UnitOfWork.Add(film);
      UnitOfWork.SaveChanges();
 
      return RedirectToAction("Index", "Home");
    }

You can see that we are just testing ModelState.IsValid to confirm if any binding errors occurred. We could also simply check film.IsValid to achieve the same thing.

This is not the only way to handle ASP.NET MVC validation, and I would point you to some of the posts David Hayden has put out on the facilities you have at your disposal along with the tutorials on the ASP.NET MVC site.

So server side validation plug’s in nicely with LightSpeed and its validation – what about if we also want some client side validation? Well thanks to a great suggestion by Justin Thirkell we can leverage jQuery and a validation plugin here along with xVal to make this nice and easy.

xVal

From Steve Sanderson’s blog: “xVal lets you link up your choice of server-side validation mechanism with your choice of client-side validation library. It guides you to fit them both into ASP.NET MVC conventions, so everything plays nicely with model binding and errors registered in ModelState”. Read more.

It is nice and flexible allowing a many to many mapping of validations as follows:

Great – so all we need to do is teach it about LightSpeed’s validation attributes.

To do this, I have used some code Justin sent through which adds a custom IRulesProvider, you can see what this looks like here: https://code.mindscape.co.nz/repos/LightSpeed/Mvc/Trunk/Src/Mindscape.LightSpeed.Mvc/Validation/LightSpeedXValRulesProvider.cs

Adding this in to my project means I need to add the following to my startup code in Global:

xVal.ActiveRuleProviders.Providers.Add(new LightSpeedXValRulesProvider());

and then a declaration on my page for xVal to apply its validation against a Film:”

<!--Html.ClientSideValidation<FilmFestival.Model.Film>()-->

and the result is I have client side validation!

Shot019

Playing along at home?

Here is the code and associated database setup script (remember to create a database called FilmFestival and run the scripts under the context of that database first or use the Build.cmd script) to cover what we have done so far. Also remember to download and install LightSpeed.

We are now also leveraging some of the community code project – Mindscape.LightSpeed.Mvc – this has been included in the Lib folder, or you can grab it independently from here.

Download FilmFestival2009_Part4.zip (94KB)

Ok – What’s next?

Next we should build out some of the remaining functionality which will probably bring up the topics of routing and some more jQuery :)

6 Responses to “MVC and LightSpeed – Part 4 – Model Binding”

  1. ASP.NET MVC Tutorial Part 4: Model binding and easy validation with xVal and LightSpeed…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. I like how tidy the controller logic is and seems to be inline with the single responsibility principle. How would you handle a scenario where the model object needs to touch the database to know if it is valid – for example having a unique email address? On a recent project I used ViewModel objects decorated with Validation Attributes (kinda like DTO’s) to handle user input. Then if valid I would pass these objects of to a service layer to co-ordinate domain validation and mapping of the DTO’s to the Model objects. Worked alright but I think I will use a automapper in the future for this, or I know you guys codegen DTO objects or something.

  3. Hi Jake :)

    We generally code gen DTO’s, and based on feedback we have added this in to the LightSpeed designer to auto-gen these if you need them.

    In terms of the scenario where you need to touch the database to validate, this would be the case for a [ValidateUnique] field – LightSpeed handles this internally for you so generally you dont need to worry about it directly.

    Assuming this isnt the case, it sounds more like some additional validation logic to be performed in the Controller action.

    LightSpeed entities also have an OnValidate() which you can override with any custom validation code as required – this is inline with pretty much every other validation framework :)

  4. [...] MVC and LightSpeed – Part 4 – Model Binding Blogged with the Flock Browser [...]

  5. A comment on putting what you have discussed into practice re Model Binder. I am doing a ‘quick’ evaluation of Lightspeed for work, and as part of that I wanted to implement a model binder (as in Linq to sql this happens out of the box).
    So your blog looked to be just what I needed. However there are a couple of issues.
    1\ Links to the source code are a little unclear (there is a link to EntityModelBinder.cs, which uses Mindscape.LightSpeed.Mvc.Helpers – so is not usable on its own – for example)
    2\ The FilmFestival example code is written in a later version of Lightspeed than production – the Mindscape.LightSpeed.Mvc.dll requires later version of Lightspeed.

  6. Hi Grayson – the EntityModelBinder class is part of the community code solution which you can grab via SVN from here: https://code.mindscape.co.nz/repos/LightSpeed/Mvc/Trunk – you will want to compile this against whichever version of LightSpeed you are currently using :)

    Alternatively you can just repurpose the class to your own needs and include it directly in your MVC app.

    Jeremy

Leave a Reply

jb's Blog