How come module-level validators are evaluated only after property-level validators?
I'm using the module-level validator: 'PropertiesMustMatch' on my view-model, like so:
[PropertiesMustMatch("Password", "PasswordConfirm")]
public class HomeIndex
{
[Required]
public string Name { get; set; }
public string Password { get; set; }
public string PasswordConfirm { get; set; }
}
I'm noticing that if I submit the form without Name filled in, the ValidationSummary() helper returns only the following error:
- The Name field is required.
However, if I fill in Name, then ValidationSummary() will return a PropertiesMustMatch error:
- 'Password' and 'PasswordConfirm' do not match.
So it looks like the property-level validators are being evaluated first, then the model-level validators.
I would much prefer if they were all validated at once, and ValidationSummary would return:
- The Name field is required.
- 'Password' and 'PasswordConfirm' do no开发者_如何学JAVAt match.
Any ideas what I can do to fix this?
I'm studying the MVC 2 source-code to try to determine why this happens.
I found what's causing this, but my "solution" is probably going to break the normal processing of validators. Use with caution.
I found a conditional return statement in the OnModelUpdated function of the DefaultModelBinder:
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
IDataErrorInfo errorProvider = bindingContext.Model as IDataErrorInfo;
if (errorProvider != null)
{
string errorText = errorProvider.Error;
if (!String.IsNullOrEmpty(errorText))
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorText);
}
}
// BEGIN CONDITION
if (!IsModelValid(bindingContext))
{
return;
}
// END CONDITION
foreach (ModelValidator validator in bindingContext.ModelMetadata.GetValidators(controllerContext))
{
foreach (ModelValidationResult validationResult in validator.Validate(null))
{
bindingContext.ModelState.AddModelError(CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName), validationResult.Message);
}
}
}
If I understand this code (which I might not) it seems that the MVC team intended model validators be skipped at this point.
I've made my own custom ModelBinder in which I re-run the code that would have been avoided by the condition:
public class CustomModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
foreach (ModelValidator validator in bindingContext.ModelMetadata.GetValidators(controllerContext))
{
foreach (ModelValidationResult validationResult in validator.Validate(null))
{
bindingContext.ModelState.AddModelError(CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName), validationResult.Message);
}
}
}
}
This seems to fix the issue.
精彩评论