views:

1286

answers:

2

Most of the tips on how to implement validation in ASP.NET MVC seem to center around the Model (either building service layers between model and controller or decorating properties of the model with validation attributes).

In my application I use ViewModels for all communication between the controllers and the views.

I have a ViewModel for my login page called 'LoginViewModel' with a property called 'EmailAddress'.

When the user enters their email address and clicks submit, this ViewModel is populated and sent to the controller, where the email address is validated.

It must be a valid email address, and the user must be from a domain that's registered with the system.

What would be a convenient way to add validation to this? Should I put the validation in the ViewModel itself? Or should it stay in the controller?

A: 

The NerdDinner tutorials show the validation as occurring in your partial classes of the model (i.e. Linq to SQL or Entity Framework). But since you're using View Models, you can put the validation logic there.

The Validation Logic doesn't go in the controller. Rather, it is hooked from the controller with a checking property, i.e. ModelState.IsValid

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {

 if (ModelState.IsValid) {

  try {
   dinner.HostedBy = "SomeUser";

   dinnerRepository.Add(dinner);
   dinnerRepository.Save();

   return RedirectToAction("Details", new { id=dinner.DinnerID });
  }
  catch {
   ModelState.AddModelErrors(dinner.GetRuleViolations());
  }
 }

 return View(new DinnerFormViewModel(dinner));
}

Full details are here:

Building the Model
http://nerddinnerbook.s3.amazonaws.com/Part3.htm

and here:

ViewData and ViewModel
http://nerddinnerbook.s3.amazonaws.com/Part6.htm

Robert Harvey
Some explanation for the downvotes would be nice.
Robert Harvey
+3  A: 

"Should I put the validation in the ViewModel itself? Or should it stay in the controller" I agree with Robert but I would add a plug for additional automation.

If you look at a tool such as xVal, you can see that routine validation (e.g., required fields, numbers within ranges, strings matching regular expressions) can be automatically done by decorating fields of your data classes. In fact, xVal can automatically write the JavaScript for routine validations so that it is carried out client side. All without writing any code. Deeper validations (e.g. is this user a member of a domain registered in our database?) happen server-side inside the model layer itself.

Using the ViewModel idiom can pose some challenges to this scheme. My current approach is to embed my entity objects inside my view model, e.g.

public class Contact {
 [Required]
 string Name { get; set; }
}

public class ContactView {
 public Contact Contact { get; set; }
 public string SomeOtherViewProperty { get; set; }
}

and then in the controller, shallow validation happens when updating the model:

UpdateModel(contactViewModel.Contact, "Contact");

and the validations requiring more information or more complicated calculations happen inside the model layer itself.

Another approach is not to embed the entity object but just map individual fields between the two. I've recently become aware of a tool called AutoMapper which automatically links fields between domain and view model objects. It looks like it should support this validation method, though I haven't used it yet.

Keith Morgan
Great ideas, Keith! At the moment I have the same strategy as you - embedding Models in ViewModels.What I would like is if both Models and ViewModels can implement validation the same way, and a JS framework such as xVal can work seamlessly with either.This would be the most flexible way. I don't think validation should be exclusively tied to the Model.
jonathanconway
I've also been using custom view models with embedded model classes, including xVal. However, there are occasions when I need my viewmodel to contain only a subset of the model's properties. I'm currently looking at Automapper to map those models. It works fine, but how to I use xVal in that scenario? Decorating the ViewModel with the MetaData attribute does not work since the AssociatedMetadataTypeTypeDescriptionProvider throws if the metadata object contains properties that are not part of the viewmodel. If you have been using Automapper, I'd like to know how you solved this problem.
Adrian Grigore