【问题标题】:How do I do Sub Object Custom Validation mvc3如何进行子对象自定义验证 mvc3
【发布时间】:2012-09-19 23:35:16
【问题描述】:

我有一个对象(标题),其中包含我想要在接受数据之前对其进行自定义验证的子对象(详细信息)列表。我已经尝试过 ModelState.IsValid 和 TryValidateModel,但它似乎没有触发子对象(仅标题对象)上的 Validate 方法。

因此,在提交时,我看到了标头的验证触发,但没有看到子项目。然后,如果我执行 TryValidateModel,我会再次看到(断点)验证方法在标题上被调用,而不是在子对象上。

带注释的验证(必须是数字等)似乎正在处理子对象,而不是通过 IValidatableObject 接口添加的自定义逻辑。任何帮助将不胜感激。

【问题讨论】:

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


    【解决方案1】:

    我创建了一个属性 ( [ValidateObject] ),它会验证你放在类上的属性,就像你认为它应该做的那样。

    public class Personne
    {
        [ValidateObject]
        public Address Address { get; set; }
        //[...]
    }
    

    (地址是一个自定义类。)

    可用于验证:

    • 模型上的对象属性。 (如上)
    • 子对象的集合。

      [ValidateObject]
      public List<Address> Address { get; set; }
      
    • 它支持多级部门,如果“地址”具有类型为“邮编”的属性和属性 [ValidateObject],它将被验证。

    代码:

    public class ValidateObjectAttribute : ValidationAttribute
    {
        public ValidateObjectAttribute()
        {
    
        }
        private ValidationContext ValidationContext { get; set; }
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            this.ValidationContext = validationContext;
            var results = new List<ValidationResult>();
    
            try
            {
                var isIterable = this.IsIterable(value);
                if (isIterable)
                {
                    int currentItemPosition = -1;
                    foreach (var objectToValidate in value as IEnumerable<object>)
                    {
                        currentItemPosition++;
                        var resultTemp = ValidationsForObject(objectToValidate, true, currentItemPosition);
                        if (resultTemp != null)
                            results.AddRange(resultTemp);
    
                    }
                    if (results.Count <= 0)
                        results = null;
                }
                else
                    results = ValidationsForObject(value);
    
                if (results != null)
                {
                    //Build a validation result 
                    List<string> memberNames = new List<string>();
                    results.ForEach(r => memberNames.AddRange(r.MemberNames));
    
                    var compositeResultsReturn = new CompositeValidationResult($"Validation for {validationContext.DisplayName} failed!", memberNames.AsEnumerable());
                    results.ForEach(r => compositeResultsReturn.AddResult(r));
    
                    return compositeResultsReturn;
                }
    
            }
            catch (Exception) { }
    
            return ValidationResult.Success;
        }
    
        private List<ValidationResult> ValidationsForObject (object objectToValidate, bool IsIterable = false, int position = -1)
        {
            var results = new List<ValidationResult>();
            var contextTemp = new ValidationContext(objectToValidate, null, null);
            var resultsForThisItem = new List<ValidationResult>();
    
            var isValid = Validator.TryValidateObject(objectToValidate, contextTemp, resultsForThisItem, true);
            if (isValid)
                return null;
    
            foreach (var validationResult in resultsForThisItem)
            {
                List<string> propNames = new List<string>();// add prefix to properties
                foreach (var nameOfProp in validationResult.MemberNames)
                {
                    if (IsIterable)
                        propNames.Add($"{this.ValidationContext.MemberName}[{position}].{nameOfProp}");
                    else
                        propNames.Add($"{this.ValidationContext.MemberName}.{nameOfProp}");
                }
                var customFormatValidation = new ValidationResult(validationResult.ErrorMessage, propNames);
                results.Add(customFormatValidation);
            }       
    
            return results;
        }
    
    
        private bool IsIterable(object value)
        {
            ////COULD WRITE THIS, but its complicated to debug...
            //if (value.GetType().GetInterfaces().Any(
            //i => i.IsGenericType &&
            //i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
            //{
            //    // foreach...
            //}
            Type valueType = value.GetType();
            var interfaces = valueType.GetInterfaces();
            bool isIterable = false;
            foreach (var i in interfaces)
            {
                var isGeneric = i.IsGenericType;
                bool isEnumerable = i.GetGenericTypeDefinition() == typeof(IEnumerable<>);
                isIterable = isGeneric && isEnumerable;
                if (isIterable)
                    break;
            }
            return isIterable;
        }
    }
    
    public class CompositeValidationResult : ValidationResult
    {
        private readonly List<ValidationResult> _results = new List<ValidationResult>();
    
        public IEnumerable<ValidationResult> Results
        {
            get
            {
                return _results;
            }
        }
    
        public CompositeValidationResult(string errorMessage) : base(errorMessage)
        {
        }
    
        public CompositeValidationResult(string errorMessage, IEnumerable<string> memberNames) : base(errorMessage, memberNames)
        {
        }
    
        protected CompositeValidationResult(ValidationResult validationResult) : base(validationResult)
        {
        }
    
        public void AddResult(ValidationResult validationResult)
        {
            _results.Add(validationResult);
        }
    }
    

    如果您的模型正确绑定,这将起作用:)

    您可能想要添加必需的属性,确保对象本身不为空。

    [Required]
    

    希望对您有所帮助!

    【讨论】:

      【解决方案2】:

      TryValidateObject 似乎没有触发自定义验证,只有数据注释?我通过执行以下操作将详细信息验证添加到标头验证中:

      foreach (var detail in this.Details)
      {
         var validationResults = detail.Validate(validationContext);
         foreach (var validationResult in validationResults)
         {
            yield return validationResult;
         }
      }
      

      这在验证方面有效,但 UI 没有显示错误消息。即使我在 UI 上有 ValidationMessagesFor。

      验证不起作用在这里解决:MVC3 Master-Details Validation not Displaying

      【讨论】:

        【解决方案3】:

        我想知道您的根对象是否存在阻止子验证的错误。 见Recursive validation using annotations and IValidatableObject 此 URL 提到了该场景以及从根目录强制对子节点进行验证的代码

        根据从根对象触​​发验证的发布

        public IEnumerable Validate(ValidationContext validationContext) { var context = new ValidationContext(this.Details, validationContext.ServiceContainer, validationContext.Items); var results = new List(); Validator.TryValidateObject(this.Details, context, results); return results; }

        【讨论】:

        • 看起来根对象没有错误(非预期和ModelState.IsValid = true)
        • 尝试其他建议,即确保根在您的 Validate 方法中验证子项,如该帖子所示,或使用基于属性的方法。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-08
        • 2022-10-05
        • 1970-01-01
        相关资源
        最近更新 更多