62

EDIT - We're using MVC4 Dev Preview....

I'm implementing an edit page for a FishingTrip class. FishingTrip contains a child collection of simple Crew objects (i.e. FishingTripID, CrewID, CrewPosition).

I'm using Jarrett Meyer's approach to add, edit and delete from the Crew collection. I'm using unobtrusive validation to specify that the properties of Crew are all Required.

My problem: when I logically-delete an item from the list (as per Jarrett's method), I don't want that item to be validated.

I have successfully tweaked the "removeRow" method on the client-side to disable unobtrusive validation for the logically-deleted item, so that the form will post despite there being an item that contains blank fields.

In my controller method [HttpPost] Edit, ModelState.IsValid starts off as false (as expected - because of the logically-deleted item that contains blank fields.) So I remove that item from my ViewModel.... but ModelState.IsValid is still false.

In summary, I (think I) want to modify my ViewModel within the controller method to remove the offending item, then call some kind of "revalidate", and have ModelState.IsValid show up as true.

Any ideas?

Merenzo
  • 5,326
  • 4
  • 31
  • 46
  • Possible duplicate of [Manually invoking ModelState validation](http://stackoverflow.com/questions/6360087/manually-invoking-modelstate-validation) – KyleMit Sep 08 '16 at 13:06

2 Answers2

144

Once you have removed the offending item(s), clear the ModelState and validate again, like so:

ModelState.Clear();
TryValidateModel(crew);  // assumes the model being passed is named "crew"

Note: Be carefull when use TryValidateModel method because this method does not validate nested object of model (As mentioned by @Merenzo).

Shmidt
  • 16,436
  • 18
  • 88
  • 136
counsellorben
  • 10,924
  • 3
  • 40
  • 38
  • 5
    Thanks @councellorben - ModelState.Clear does indeed remove those errors (and the whole ModelState.Keys collection) but TryModelValidate doesn't seem to repopulate the ModelState. It doesnt seem to do anything? e.g. If I don't remove the offending item, then call .Clear, then TryValidateModel, I'm left with a blank ModelState, rather than one that matches the "pre-Clear()" version. – Merenzo Oct 20 '11 at 23:54
  • If you review the MVC source code (TryValidateModel in Controller.cs in System.Web.Mvc), TryValidateModel adds any errors to the ModelState, and I have used it for that purpose in seveal projects. – counsellorben Oct 21 '11 at 01:48
  • @councellorben - in your projects, does Clear() blow away all ModedlState.Keys, and does TryValidateModel then restore all these Keys *and* add the errors to the Keys? (Apologies - we're using MVC4 not MVC3 as I originally stated.) – Merenzo Oct 26 '11 at 00:23
  • In MVC 3, all keys are restored, and the errors are added. I will have to test in MVC 4 to see if this has changed. That would be a rather serious breaking change. – counsellorben Oct 26 '11 at 02:13
  • 6
    In my MVC4 project, I mistakenly thought TryValidateModel() was doing nothing... however it's just ignoring nested objects (as per http://stackoverflow.com/questions/4465432/). In my MVC4 project, TryValidateModel() is restoring those keys that are (a) in the top level object and (b) only if they have validation errors. So it looks to me like this answer is right. Whether or not there's a breaking change in MVC4 is another story :) – Merenzo Oct 26 '11 at 08:42
  • Excellent catch. I missed it, because I used TryValidateModel in several circumstances while iterating through a collection of models. – counsellorben Oct 26 '11 at 10:48
  • 2
    So, how do you replicate the deep-validation that MVC does to a model on it's way in? I'm using FluentValidation for everything... but if I apply the validator directly it doesn't update modelstate. – Mir Feb 28 '13 at 18:44
  • 1
    It took me so long to find out how to properly do this. Thank you for saving me another day of grief. – Mike Caputo Jun 25 '13 at 20:07
  • 3
    you can use TryValidateModel overload method TryValidateModel(model.NestedModel, "NestedModel") – pirimoglu Feb 07 '16 at 05:17
  • Any ideas on how to avoid "Value cannot be null" error in unit test with this method? – Morgeth888 Oct 16 '20 at 19:29
3

Late to the game, but still: I was also looking for a way to validate model after doing some tweaks to it (more precisely - to the items of its nested collection) - and TryValidateModel didn't work for me, as it doesn't process nested objects.

Finally, I settled with custom model binder:

public class MyItemModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(
        ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(MyItemModel))
        {
            MyItemModel item = (MyItemModel)bindingContext.Model;
            //do required tweaks on model here 
            //(I needed to load some additional data from DB)
        }
        //validation code will be called here, in OnModelUpdated implementation
        base.OnModelUpdated(controllerContext, bindingContext);
    }
}

on the model class:

[ModelBinder(typeof(MyItemModelBinder))]
public class MyItemModel : IValidatableObject
{
    //...
}
lxa
  • 3,234
  • 2
  • 30
  • 31
  • I'm trying to get the DataAnnotation attributes to validate again, but they don't seem to after OnModelUpdated. Any ideas? – xr280xr Jun 22 '15 at 22:02
  • I had created and applied a custom model binder to the parent view model. I needed to apply it to the child VM that directly contained the validation. – xr280xr Jun 22 '15 at 22:49