0

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

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

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

4

3 回答 3

1

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

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

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

它可用于验证:

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

    [ValidateObject]
    public List<Address> Address { get; set; }
    
  • 它支持多级部门,如果“地址”具有类型为“ZipCode”且属性为 [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]

希望对您有所帮助!

于 2017-10-16T17:04:48.807 回答
0

我想知道您的根对象是否存在阻止子验证的错误。请参阅Recursive validation using annotations 和 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;
    }

于 2012-09-28T05:38:48.703 回答
0

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

于 2012-10-08T05:08:15.790 回答