ASP.NET MVC3 TryValidateModel validates the entire collection of models, not just one instance

I have an action that accepts a list of models. I would like to test each separately in comparison with the entire collection of models at once. I am trying to use TryValidateModel, but it seems that if any of my models are invalid, they are all invalid. My form displays 5 SurveyResponseModels (a class with two required lines and two ints). If I completely fill in all five models, I will get validCount = 5 below. However, if any of the five models is incomplete (thus, validation fails), I get validCount 0. Is the expected behavior of TryValidateModel? If so, any ideas on how I can verify this data at a time?

[HttpPost] public ActionResult Create(IList<SurveyResponseModel> respondents) { int validCount = 0; foreach (SurveyResponseModel respondent in respondents) { if (TryValidateModel(respondent)) { validCount++; } } ModelState.AddModelError("", validCount.ToString() + " respondents passed validation"); } 
+4
source share
4 answers

Looking at the code, it seems to me that TryValidateModel will check all models of the type specified by the provided object, and not just this specific object. Moreover, it returns the current value of the ModelState.IsValid property, so when there is an invalid model, all calls to TryValidateModel will return false. If you want to do something like this, I think you will need to run and check the validators for each instance of the model on that particular instance of the model.

I also think that model validators were already running by the time you manually call them. You can verify this (for an invalid model) by looking at the ModelState.IsValid value before your loop. If it is false, it means that the validators were executed using the model’s connecting device, which, it seems to me, is happening.

You can find the source code for MVC http://aspnet.codeplex.com/ .

+4
source

TryValidateModel() adds validation to the list of errors. Use ModelState.Clear() to remove previous errors. Validation is performed as part of the model binding process automatically if the [ValidateInput(false)] attribute is not used. See fooobar.com/questions/400512 / ... for more details.

+4
source

I saw this behavior in another post.

To continue tvanfosson's suggestion, I suggest you put a breakpoint in front of your foreach and check ModelState to see if there are any errors there. They are probably: DefaultModelBinder placed them. Then I can imagine 2 possibilities:

  • Errors are transmitted correctly, in some way identifies each responder (I do not think so). In this case, a simple linq expression can give you a distinct number of problematic respondents.
  • Errors are mixed, for example, if 2 respondents have an invalid name, you have only one error for the name, or you cannot distinguish which responder. In this case, you need to create your own validation logic. You can create an object of type RespondentListViewModel and create a RespondentListViewModelBinder. You can see the source code above to find out how DefautlModelBinder checks the lists and adds model errors.

Yes, this is this part of ASP.NET MVC where the binding logic does not do what we want it to do ...

+1
source

source:

  protected internal bool TryValidateModel(object model, string prefix) { if (model == null) { throw new ArgumentNullException("model"); } ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()); foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(metadata, ControllerContext).Validate(null)) { ModelState.AddModelError(DefaultModelBinder.CreateSubPropertyName(prefix, validationResult.MemberName), validationResult.Message); } return ModelState.IsValid; } 

It will always return false if you are mistaken.

+1
source

Source: https://habr.com/ru/post/1337044/


All Articles