验证需要其他属性值的属性

Validation of properties that require the values of other properties

所以我查看了这个答案ASP:NET MVC 4 dynamic validation of a property depending of the current value of another property,它没有涵盖我遇到的问题。

我正在使用服务器端验证。我有一个要求...

A value is only required if another property is specified

问题

MVC 绑定每个 属性 并在绑定它们时调用 属性 上的每个验证器。如果我在检查 validationContext.ObjectInstance.[MY_DEPENDENT_PROPERTY] 时依赖于设置的多个属性,则可能这些依赖属性尚未绑定。

我需要的是验证 after 绑定的验证属性 - 如果存在的话。


所以这是一个简单的例子来解释我的情况(不打算执行,因为它很可能没问题,因为问题与绑定顺序有关)

我的模型

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;
    }
}

我的验证者

基本上这里发生的是它调用模型上的静态方法以查看它是否应该验证。

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}'");
    }
}

所以我遇到的这个问题是我继承自 RequiredAttribute。 MVC 在内部以不同于其他任何方式处理此属性。

当 Model Binder 循环遍历属性时,它获取 RequiredAttributes 并同时执行它们...

// 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);
            }
        }
....

v.IsRequired 实际上解析为测试当前属性是否为 RequiredAttribute 的行,并将在当前不完整的模型状态下验证它。

通过继承 ValidationAttribute 它 运行 模型构建后的验证并解决了我的问题。


感谢@StephenMuecke 提示我这样做。