Ok, so this one is not really a “Quick” tip. It’s a bit long, so I’m dividing it into two posts to make it a bit more readable.

The objective is to build a solid Edit view with both client and server-side validation of data, as well as showing you how to avoid some common pitfalls I see being frequently made in ASP.NET MVC implementations.

To make this easy to understand I’ll detail all the steps required to build this application, and I also include a link to the source, so you can try it out for yourself.

First off, we’ll be using a real database instead of simple objects for this demo. The database is simple enough and after using Entity Framework to generate a model for me, this is the result:

EF Model

Let’s start by defining a simple List view for all the Authors in my database. To do that, I changed the Index action method of the Home controller to this:

public ActionResult Index()

{

    using (BulletProofDemoEntities ctx = new BulletProofDemoEntities())

    {

        var authorsList = ctx.Authors.ToList();

        return View(authorsList);

    }

}


I’ll use the default List template included in Visual Studio to add a strongly-typed view for the Author class. Without making any changes, I should get this result when I run the application:

Index View

If we now click the Edit link that is next to each item, we will obviously go nowhere as we haven’t implemented that action method. Let’s do that now:

public ActionResult Edit(int id)

{

    using (BulletProofDemoEntities ctx = new BulletProofDemoEntities())

    {

        var author = ctx.Authors.First(a => a.Id == id);

        return View(author);

    }

}


I also asked Visual Studio to generate a strongly-typed view for the Author class, using the default Edit template and naming it “Edit”. If we now launch our application again and click on an Edit link, we’ll get this result:

Edit View

The Save button is a simple submit button that will post to the same url. So clicking on it now just forces a refresh of this page because we’ll basically execute the same action method. To implement the Save behavior, we need to write the following code:

[HttpPost]

public ActionResult Edit(Author updatedAuthor)

{

    using (BulletProofDemoEntities ctx = new BulletProofDemoEntities())

    {

        var author = ctx.Authors.First(a => a.Id == updatedAuthor.Id);

        ctx.ApplyCurrentValues("Authors", updatedAuthor);

        ctx.SaveChanges();

 

        return RedirectToAction("Index");

    }

}


Things to notice in this piece of code:

·        The HttpPost attribute informs the engine that this Edit action method should handle POST requests, and basically sets it apart from the other Edit method we already had.

 

·        We receive an Author object, although we are basically handling a form post with usual data. This works because ASP.NET MVC uses a Model Binder that matches the form post data with the properties in the specified class, doing the nasty work for us and allowing us to work with a familiar model (more on this later)

 

·        After we save the changes through normal Entity Framework methods we use the PRG pattern (POST-REDIRECT-GET) which is a best practice that avoids repetition of POST requests if the user clicks refresh.

Let’s test our application now:

Edit View with Save enabled

Notice I’m changing both the ContributorPoints and the Bio fields. If I click Save:

Updated Information

So, I’m now back at the Index view and the information about the author has been changed. Everything is working, however…what if the user introduces invalid data?

Let’s say we try to introduce a string in the ContributorPoints field:

Bad input

What we would normally expect after clicking Save was for an exception to be throw right?

However, the result is different:

Back at index view, contributorpoints down to 0

Not only did we NOT get an exception, the ContributorPoints has been brought to 0. That is a bad thing to happen and doesn’t make much sense…at least the database would throw an error if we tried to add a string to an int column.

This is related to the way the Model Binder works. It looks at the properties of your class, matches the properties to your form post data and returns an object for you to use. Obviously, trying to set the value of the ContributorPoints property is an error, but the Model Binder doesn’t generate an exception because it wants you to have a chance on seeing what happened. However, we didn't check that so the ContributorPoints remained with the default int value, which is 0.

So, how do we see which errors occurred during the binding?

[HttpPost]

public ActionResult Edit(Author updatedAuthor)

{

    if (!ModelState.IsValid)

        return View(updatedAuthor);

 

    using (BulletProofDemoEntities ctx = new BulletProofDemoEntities())

    {

        var author = ctx.Authors.First(a => a.Id == updatedAuthor.Id);

        ctx.ApplyCurrentValues("Authors", updatedAuthor);

        ctx.SaveChanges();

 

        return RedirectToAction("Index");

    }

}


The Model Binder writes that information in the ModelState object, which can be checked to see if there any errors. Notice that I’m checking if the ModelState is valid, and returning the same view if any errors occurred. The result of trying to add invalid data is now this:

validation being performed

The validation was done server-side, however, how View also manages to display the error. That is because the ModelState object is made available to Views as well, so we have this nice validation message and coloring of the textbox (through CSS).

If you look inside your Edit view, you’ll see that the rendering of this field is done like this:

<div class="editor-field">

    <%= Html.TextBoxFor(model => model.ContributorPoints) %>

    <%= Html.ValidationMessageFor(model => model.ContributorPoints) %>

</div>


Note: I’m using VS 2010 Release Candidate. In Beta 2 the default views are slightly different. In that case you would see just a ‘*’ next to the checkbox and the validation message would be displayed in a Validation Summary on top of the view.

What if we want to do the validation on client-side? In MVC 1 you couldn’t without writing your own validation scheme in javascript. In MVC 2 however, we included a client-side validation framework. The framework uses AJAX to fetch the validation rules from the server and generates the needed javascript to perform validation.

To use it, simply change the Edit view like this:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

   <script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>

   <script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>

 

    <h2>Edit</h2>

   

    <% Html.EnableClientValidation(); %>

    <% using (Html.BeginForm()) {%>


We added two script references (a better place might be the site.master) and we use the Html.EnableClientValidation method. That’s it!

If we now try our Edit form, putting a string in the ContributorPoints field and tabbing out will display the message the same way we saw before.

The validation framework is extensible and you can use other libraries for client-side validation including JQuery.

To sum it up, in the first part of this Quick Tip we added an Edit view for our Model, and we enabled both server-side and client-side validation of data. We still have to change our view so that we can change the Community assigned to the Author, but that will be covered in the second part of the Quick Tip.

In this example we’re just using very simple validation that is provided by checking the property types. In part 2 of this Quick Tip, we’ll add custom validation to the other fields using Data Annotations, and I will also show you what happens when you don’t protect your model (here’s a really quick tip: bad things can happen).

Until then, have fun!