【问题标题】:MVC3 Custom validation attribute that compare valid minimum and maximum values比较有效最小值和最大值的 MVC3 自定义验证属性
【发布时间】:2014-09-14 19:14:30
【问题描述】:

我的模型有 2 个十进制参数,如下所示:

public class Range
{
     public decimal MinimumValue { get; set; }
     public decimal MaximumValue { get; set; }
}

是否可以对两个参数进行自定义验证,其中:

  • 验证 MinimmumValue(必须小于 MaximumValue)
  • MaximumValue 验证(必须大于 MinimumValue)

【问题讨论】:

  • 您使用哪种验证方式?数据注释属性?
  • 哦是的数据注释

标签: c# asp.net-mvc-3 validation


【解决方案1】:

有很多关于创建自定义验证属性的文章,但这里有一个示例,说明它在您的情况下的外观:

public class GreaterThanAttribute : ValidationAttribute
{
    public string PropertyNameToCompare { get; set; }

    public GreaterThanAttribute(string propertyNameToCompare)
    {
        PropertyNameToCompare = propertyNameToCompare;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var propertyToCompare = validationContext.ObjectType.GetProperty(PropertyNameToCompare);
        if (propertyToCompare == null)
        {
            return new ValidationResult(
                string.Format("Invalid property name '{0}'", PropertyNameToCompare));
        }
        var valueToCompare = propertyToCompare.GetValue(validationContext.ObjectInstance, null);

        bool valid;

        if (value is decimal && valueToCompare is decimal)
        {
            valid = ((decimal) value) > ((decimal) valueToCompare);
        }
        //TODO: Other types
        else
        {
            return new ValidationResult("Compared properties should be numeric and of the same type.");
        }

        if (valid)
        {
            return ValidationResult.Success;
        }

        return new ValidationResult(
            string.Format("{0} must be greater than {1}",
                validationContext.DisplayName, PropertyNameToCompare));
    }
}

我不太喜欢我开始检查属性类型的地方,但我不知道是否有可能让它变得更好。

当然你也需要实现GreaterThanAttribute

【讨论】:

    【解决方案2】:

    如果选中复选框,我做了一个自定义属性来验证字段。如果未选中复选框,则该字段为必填项,如果未选中,则该字段不是必需的。我可以使用此代码来调整和检查一个字段是否比其他字段更大。 您还可以使用数据注释扩展。有关数据注释扩展的更多信息here

    public class RequiredIf : ConditionalValidationAttribute
    {
        protected override string ValidationName
        {
            get { return "requiredif"; }
        }
    
        public RequiredIf(string dependentProperty, object targetValue)
            : base(new RequiredAttribute(), dependentProperty, targetValue)
        {
        }
    
        protected override IDictionary<string, object> GetExtraValidationParameters()
        {
            return new Dictionary<string, object> 
                        { 
                            { "rule", "required" }
                        };
        }
    }
    
    
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
    public abstract class ConditionalValidationAttribute : ValidationAttribute, IClientValidatable
    {
        protected readonly ValidationAttribute InnerAttribute;
        public string DependentProperty { get; set; }
        public object TargetValue { get; set; }
        protected abstract string ValidationName { get; }
    
        protected virtual IDictionary<string, object> GetExtraValidationParameters()
        {
            return new Dictionary<string, object>();
        }
    
        protected ConditionalValidationAttribute(ValidationAttribute innerAttribute, string dependentProperty, object targetValue)
        {
            this.InnerAttribute = innerAttribute;
            this.DependentProperty = dependentProperty;
            this.TargetValue = targetValue;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // get a reference to the property this validation depends upon
            var containerType = validationContext.ObjectInstance.GetType();
            var field = containerType.GetProperty(this.DependentProperty);
            if (field != null)
            {
                // get the value of the dependent property
                var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);
    
                // compare the value against the target value
                if ((dependentvalue == null && this.TargetValue == null) || (dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
                {
                    // match => means we should try validating this field
                    if (!InnerAttribute.IsValid(value))
                    {
                        // validation failed - return an error
                        return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
                    }
                }
            }
            return ValidationResult.Success;
        }
    
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule()
            {
                ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                ValidationType = ValidationName,
            };
            string depProp = BuildDependentPropertyId(metadata, context as ViewContext);
            // find the value on the control we depend on; if it's a bool, format it javascript style
            string targetValue = (this.TargetValue ?? "").ToString();
            if (this.TargetValue.GetType() == typeof(bool))
            {
                targetValue = targetValue.ToLower();
            }
            rule.ValidationParameters.Add("dependentproperty", depProp);
            rule.ValidationParameters.Add("targetvalue", targetValue);
            // Add the extra params, if any
            foreach (var param in GetExtraValidationParameters())
            {
                rule.ValidationParameters.Add(param);
            }
            yield return rule;
        }
    
        private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
        {
            string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
            // This will have the name of the current field appended to the beginning, because the TemplateInfo's context has had this fieldname appended to it.
            var thisField = metadata.PropertyName + "_";
            if (depProp.StartsWith(thisField))
            {
                depProp = depProp.Substring(thisField.Length);
            }
            return depProp;
        }
    }
    

    【讨论】:

    • 但是如果说我想使用数据注释,结构会有所不同吧?
    • 关于什么结构?使用就像一个普通的注释。您将放置 [RequiredIf] 属性。在我的示例中,您可以像这样使用: [RequiredIf("OnRequest", true, ErrorMessageResourceName = "ValueRequired", ErrorMessageResourceType = typeof(Resources.Propertie))]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-09
    • 2021-04-16
    • 2017-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多