问题
So I have checked out this answer ASP:NET MVC 4 dynamic validation of a property depending of the current value of another property and it does not cover the issue I am having.
I am using server side validation. I have a requirement that...
A value is only required if another property is specified
Issue
MVC binds each property and calls each validator on that property as it binds them. If I am dependent on multiple properties being set when I check validationContext.ObjectInstance.[MY_DEPENDENT_PROPERTY]
there is a possibility that those dependent properties have not been bound yet.
What I need is a validation attribute that validates after binding - if that even exists.
So here is a simple example to explain my situation (not intended to be executed as it will more than likely be fine since the issue has to do with binding order)
My model
public class Address
{
[Required]
public string ResidentialAddress { get; set; }
public bool PostalIsTheSameAsResidential { get; set; }
// will only be required if PostalIsTheSameAsResidential is false.
// see the static method below and RequiredIfAttribute
[RequiredIf(typeof(Address), nameof(PostalRequiredIfNotSameAsResidential)]
public string PostalAddress { get; set; }
public static bool PostalRequiredIfNotSameAsResidential(Address model)
{
return !model.PostalIsTheSameAsResidential;
}
}
My validator
Essentially what happens here is it calls the static method on the model to see whether it should validate.
public sealed class RequiredIfAttribute : RequiredAttribute
{
private readonly MethodInfo _validationMethod;
public override bool RequiresValidationContext => true;
public RequiredIfAttribute(Type type, string methodName)
{
this._validationMethod = type.GetMethod(methodName);
if (this._validationMethod == null)
{
throw new MethodAccessException($"The validation method '{methodName}' does not exist on type '{type}");
}
}
public override bool IsValid(object value)
{
throw new NotSupportedException();
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult result = ValidationResult.Success;
var parameters = this._validationMethod.GetParameters();
var returnType = this._validationMethod.ReturnType;
if (returnType == typeof(bool) && parameters.Length == 1 && parameters[0].ParameterType == validationContext.ObjectType)
{
if ((bool)_validationMethod.Invoke(null, new object[] { validationContext.ObjectInstance }))
{
if (!base.IsValid(value))
{
string[] memberNames;
if (validationContext.MemberName == null)
{
memberNames = null;
}
else
{
memberNames = new string[1];
memberNames[0] = validationContext.MemberName;
}
result = new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName), memberNames);
}
}
return result;
}
var expectedFuncType = typeof(Func<,>).MakeGenericType(validationContext.ObjectType, typeof(bool));
throw new MethodAccessException($"The validation method '{this._validationMethod}' does not have the correct definition. Expected '{expectedFuncType}'");
}
}
回答1:
So this issue that I was having was that I was inheriting from the RequiredAttribute
. Internally MVC handles this attribute differently to everything else.
When the Model Binder is looping through the properties, it gets the RequiredAttribute
s and executes them at the same time...
// System.Web.Mvc.DefaultModelBinder.SetProperty
....
ModelValidator modelValidator = (from v in ModelValidatorProviders.Providers.GetValidators(modelMetadata, controllerContext)
where v.IsRequired
select v).FirstOrDefault<ModelValidator>();
if (modelValidator != null)
{
foreach (ModelValidationResult current in modelValidator.Validate(bindingContext.Model))
{
bindingContext.ModelState.AddModelError(key, current.Message);
}
}
....
That v.IsRequired
actually resolves to a line that tests if the current attribute is a RequiredAttribute
and will validate it there, in the current, incomplete model state.
By inheriting from ValidationAttribute
it ran the validations after the model had been built and solved my issue.
Thanks to @StephenMuecke for prompting me with this.
来源:https://stackoverflow.com/questions/40478695/validation-of-properties-that-require-the-values-of-other-properties