Luke
Luke

Reputation: 23690

What is the point of the MVC implementation of IValidatableObject if it can't be used to perform full validation of the object?

Please can someone help me, because I'm having real trouble getting on with validating my objects that have bound in my controller actions in one swoop.

I thought that IValidatableObjects Validate() method would fire every time the binding takes place, but this isn't true... if there is a general model error it won't fire.

This leads me to wonder, how am I supposed to perform a full complex validation on my object and return the full set of validation errors? No one wants to fix all of the reported errors on a web form, then submit it to have more returned to them.

I thought I might just be able to perform all of my validation in the Validate() method, but this isn't true because seemingly there is no way of getting away from the general validation of models. For example if you were to attempt to bind a string to an int, it doesn't silently fail, it adds a model validation error and then doesn't fire Validate to perform further validation on the object.

So I can't perform all of my validation just using one method:


  1. General model validation using validation attributes
    • Reasons
    • Can't perform advanced validation, such as conditional validation based on other values within the model
    • It isn't designed to perform anything more than individual field validation

  1. Implement IValidatableObject and perform full validation in Validate() method
    • Reasons
    • There is no getting away or 'switching off' the general model validation so that it fails silently so that I can perform full validation on the object
    • IValidatebleObject isn't always fired if general model validation fails in any way, such as a failed binding

How can I perform full validation of my object in one go, no matter whether the binding was successful or not?

Why doesn't IValidatableObject fire the Validation() method regardless of the binding success?

Upvotes: 2

Views: 1473

Answers (2)

Erik Funkenbusch
Erik Funkenbusch

Reputation: 93444

The problem you seem to be running into is that validations occur in different parts of the framework for different reasons.

First, client validation occurs. If all your fields have unobtrusive client validation, then all validation will occur at once on the client.

Second comes model binding. If an error occurs in trying to bind an item to it's model entry, then those items will first fail. For instance, if you try to bind the string "xxx" to a DateTime, it's going to throw a validation error because it cannot convert "xxx" to a DateTime. And, since DateTime is non-nullable, it can't simply put null there.

Third comes your actual server-side data attribute validations. If you have only partial client-side validation (meaning not all fields have client-side validation) then you can get the odd situation that it throws validation errors for some items client-side, then after the user has fixed those problems and submits, the server-side validation finds errors and you get more errors thrown.

Finally, IValidatableObject is called. Unfortunately, IValidatableObject has no client-side validation, so you would either have to create client-side validation for these fields, or completely disable client-side validation in order to get server-side validation to occur all at once.

IValidatableObject is a rather general purpose interface, and is used in more places than just MVC. MVC uses it because it's a convenient already existing interface. And, it was present before client-side validation was added to MVC. A better approach is to create a ValidationAttribute derived attribute that implements client-side validation, then supplying the proper javascript plug-ins for unobtrusive-validation.

If validation fails in any of the steps, it does not go on to the next step. ie., if client-side validation fails, it doesn't call server-side. If Data attributes fail, it doesn't call IValidatableObject.

One way around this is described in this post:

How to force MVC to Validate IValidatableObject

Upvotes: 2

Robert Anderson
Robert Anderson

Reputation: 1256

A few different options come to mind. If you need complex validation to be done on the client use of a javascript framework such as Angular or Knockout would be beneficial, or failing that adding a jQuery validation plugin such as: http://jqueryvalidation.org/

It may also be worthwhile reviewing the ModelStateDictonary methods and seeing if these could be of help - e.g. manually calling ModelState.AddModelError for complex validation scenarios in your controller.

http://msdn.microsoft.com/en-us/library/system.web.mvc.modelstatedictionary_methods(v=vs.118).aspx

example:

 bool valid = true;

 if (string.IsNullOrEmpty(model.JobNumber))
 {
     ModelState.AddModelError("jobNumber", "Please enter job number");
     valid = false;
 }

 return View(model);

Upvotes: 1

Related Questions