c# - 如果属性中有验证属性,为什么 Validator.TryValidateObject 不验证类?

标签 c# validation annotations validationattribute

我创建了一个针对类的自定义 ValidationAttribute。每当我尝试调用 Validator.TryValidateObject 时,这都会正确验证。但是当我的类中的属性中有其他 ValidationAttribute 时,验证结果不包含类级别验证的结果。

这是一个示例代码:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class IsHelloWorldAttribute : ValidationAttribute
{
    public object _typeId = new object();
    public string FirstProperty { get; set; }
    public string SecondProperty { get; set; }

    public IsHelloWorldAttribute(string firstProperty, string secondProperty)
    {
        this.FirstProperty = firstProperty;
        this.SecondProperty = secondProperty; 
    }

    public override bool IsValid(object value)
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
        string str1 = properties.Find(FirstProperty, true).GetValue(value) as string;
        string str2 = properties.Find(SecondProperty, true).GetValue(value) as string;

        if (string.Format("{0}{1}", str1,str2) == "HelloWorld")
            return true;
        return false;
    }

    public override object TypeId
    {
        get
        {
            return _typeId;
        }
    }
}

这是我需要验证的类的代码

[IsHelloWorld("Name", "Code", ErrorMessage="Is not Hello World")]
public class MyViewModel : BaseViewModel
{
    string name;
    string code;

    [Required]
    public string Name
    {
        get { return model.Name; }
        set
        {
            if (model.Name != value)
            {
                model.Name = value;
                base.RaisePropertyChanged(() => this.Name);
            }
        }
    }        

    public string Code
    {
        get { return code; }
        set
        {
            if (code != value)
            {
                code = value;
                base.RaisePropertyChanged(() => this.Code);
            }
        }
    }
}

下面是我如何调用 TryValidateObject 方法:

            var validationContext = new ValidationContext(this, null, null);               
            var validationResults = new List<ValidationResult>();               
            Validator.TryValidateObject(this, validationContext, validationResults, true);

现在,如果我在 Name 属性中有 [Required] 属性,并且我尝试调用 Validator.TryValidateObject,验证结果只有一个,这就是 Required 验证的结果。但是当我从 Name 中删除 [Required] 属性并保留 IsHellowWorld 属性然后调用 TryValidateObject 时,它会给我一个结果,那就是 HellowWorldValidation 的结果。

我需要做的是在类级别和属性级别上获得所有验证。我可以在不实现我自己的 TryValidateObject 方法的情况下实现这一目标吗?

最佳答案

这是因为如果检测到属性错误,检查就会短路。这是有道理的,因为类级别验证可能更昂贵,可能涉及回调、其他数据源调用等。

如果检测到属性错误,则逻辑会停止。

在 System.ComponentModel.DataAnnotations.Validator 源代码中:

public static bool TryValidateObject(object instance, ValidationContext validationContext, 
  ICollection<ValidationResult> validationResults, bool validateAllProperties)
{
  if (instance == null)
    throw new ArgumentNullException("instance");
  if (validationContext != null && instance != validationContext.ObjectInstance)
    throw new ArgumentException(DataAnnotationsResources.
    Validator_InstanceMustMatchValidationContextInstance, "instance");
  bool flag = true;
  bool breakOnFirstError = validationResults == null;
  foreach (Validator.ValidationError validationError in 
    Validator.GetObjectValidationErrors(instance, validationContext, 
    validateAllProperties, breakOnFirstError))
  {
    flag = false;
    if (validationResults != null)
      validationResults.Add(validationError.ValidationResult);
  }
  return flag;
}

请注意对 Validator.GetObjectValidationErrors 的调用,它又定义为:

private static IEnumerable<Validator.ValidationError> GetObjectValidationErrors(
  object instance, ValidationContext validationContext, 
  bool validateAllProperties, bool breakOnFirstError)
{
  if (instance == null)
    throw new ArgumentNullException("instance");
  if (validationContext == null)
    throw new ArgumentNullException("validationContext");
  List<Validator.ValidationError> list = new List<Validator.ValidationError>();

  //Check for property errors here
  list.AddRange(Validator.GetObjectPropertyValidationErrors(instance, validationContext,
    validateAllProperties, breakOnFirstError));

  // Short circuits here if any found
  if (Enumerable.Any<Validator.ValidationError>(
    (IEnumerable<Validator.ValidationError>) list))
    return (IEnumerable<Validator.ValidationError>) list;

  // Class level validation occurs below this point
  IEnumerable<ValidationAttribute> validationAttributes = 
    Validator._store.GetTypeValidationAttributes(validationContext);
  list.AddRange(Validator.GetValidationErrors(instance, validationContext, 
    validationAttributes, breakOnFirstError));
  if (Enumerable.Any<Validator.ValidationError>(
    (IEnumerable<Validator.ValidationError>) list))
    return (IEnumerable<Validator.ValidationError>) list;
  IValidatableObject validatableObject = instance as IValidatableObject;
  if (validatableObject != null)
  {
    foreach (ValidationResult validationResult in 
      Enumerable.Where<ValidationResult>(validatableObject.Validate(validationContext), 
      (Func<ValidationResult, bool>) (r => r != ValidationResult.Success)))
      list.Add(new Validator.ValidationError((ValidationAttribute) null, instance, 
        validationResult));
  }
  return (IEnumerable<Validator.ValidationError>) list;
}

关于c# - 如果属性中有验证属性,为什么 Validator.TryValidateObject 不验证类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21571162/

相关文章:

c# - 使用 lambda 为通用列表中的每个对象执行方法

c# - 如何从代码隐藏更改 VisualBrush 中使用的 StaticResource 以响应正确的用户输入?

javascript - Jquery Ajax 验证复选框

javascript - 验证不适用于具有 'Required' 属性的文件输入 - AngularJS

java - 如何用Java处理来自客户端的Websocket消息?

c# - 为什么我的线程安全字典实现会产生数据竞争?

c# - 如何从 C# 字符串中删除一些字符?

javascript - 防止用户在小数点前 2 位数字后输入更多数字

java - 如何在 Java Annotation 中设置字符串数组

python - 我应该如何在 Python 中注释返回 ctypes 数组的函数?