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

时间:2016-11-08 03:56:40

标签: c# asp.net-mvc model-binding asp.net-mvc-validation

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

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

  

仅在指定了其他属性时才需要值

问题

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

我需要的是一个验证属性,用于在绑定后验证 - 如果它甚至存在。

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

我的模特

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

1 个答案:

答案 0 :(得分:1)

所以我遇到的这个问题是我继承了RequiredAttribute。内部MVC以不同的方式处理此属性。

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

// 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提醒我这件事。