我如何使用IValidatableObject?

zrg*_*zrg 163 c# asp.net ivalidatableobject

据我所知,IValidatableObject它用于以一种方式验证对象,让人们相互比较属性.

我仍然希望有属性来验证单个属性,但我想在某些情况下忽略某些属性的失败.

我是否试图在下面的情况下错误地使用它?如果不是我如何实现这个?

public class ValidateMe : IValidatableObject
{
    [Required]
    public bool Enable { get; set; }

    [Range(1, 5)]
    public int Prop1 { get; set; }

    [Range(1, 5)]
    public int Prop2 { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (!this.Enable)
        {
            /* Return valid result here.
             * I don't care if Prop1 and Prop2 are out of range
             * if the whole object is not "enabled"
             */
        }
        else
        {
            /* Check if Prop1 and Prop2 meet their range requirements here
             * and return accordingly.
             */ 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

zrg*_*zrg 155

首先,感谢@ paper1337指出我正确的资源...我没有注册,所以我不能投票给他,如果有人读到这个,请这样做.

以下是如何完成我想要做的事情.

可验证类:

public class ValidateMe : IValidatableObject
{
    [Required]
    public bool Enable { get; set; }

    [Range(1, 5)]
    public int Prop1 { get; set; }

    [Range(1, 5)]
    public int Prop2 { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();
        if (this.Enable)
        {
            Validator.TryValidateProperty(this.Prop1,
                new ValidationContext(this, null, null) { MemberName = "Prop1" },
                results);
            Validator.TryValidateProperty(this.Prop2,
                new ValidationContext(this, null, null) { MemberName = "Prop2" },
                results);

            // some other random test
            if (this.Prop1 > this.Prop2)
            {
                results.Add(new ValidationResult("Prop1 must be larger than Prop2"));
            }
        }
        return results;
    }
}
Run Code Online (Sandbox Code Playgroud)

Validator.TryValidateProperty()如果验证失败,则使用将添加到结果集合.如果没有验证失败,则不会向结果集合添加任何内容,这表示成功.

做验证:

    public void DoValidation()
    {
        var toValidate = new ValidateMe()
        {
            Enable = true,
            Prop1 = 1,
            Prop2 = 2
        };

        bool validateAllProperties = false;

        var results = new List<ValidationResult>();

        bool isValid = Validator.TryValidateObject(
            toValidate,
            new ValidationContext(toValidate, null, null),
            results,
            validateAllProperties);
    }
Run Code Online (Sandbox Code Playgroud)

validateAllProperties将此方法设置为false 非常重要.如果validateAllProperties为false,则仅[Required]检查具有属性的属性.这允许该IValidatableObject.Validate()方法处理条件验证.

  • 为了增强这个答案,可以使用反射来查找具有验证属性的所有属性,然后调用 TryValidateProperty。 (2认同)

Chr*_*uts 76

引用杰夫汉德利关于验证对象和属性的博客文章与验证器:

验证对象时,Validator.ValidateObject中应用以下过程:

  1. 验证属性级属性
  2. 如果任何验证器无效,则中止验证返回失败
  3. 验证对象级属性
  4. 如果任何验证器无效,则中止验证返回失败
  5. 如果在桌面框架上并且该对象实现了IValidatableObject,则调用其Validate方法并返回任何失败

这表明您尝试做的事情不会开箱即用,因为验证将在步骤#2中止.您可以尝试创建从内置属性继承的属性,并在执行正常验证之前专门检查是否存在已启用的属性(通过接口).或者,您可以将所有逻辑用于验证Validate方法中的实体.


Stu*_*tLC 33

只是添加几点:

因为Validate()方法签名返回IEnumerable<>,yield return可以用于懒惰地生成结果 - 如果某些验证检查是IO或CPU密集型,这将是有益的.

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    if (this.Enable)
    {
        // ...
        if (this.Prop1 > this.Prop2)
        {
            yield return new ValidationResult("Prop1 must be larger than Prop2");
        }
Run Code Online (Sandbox Code Playgroud)

此外,如果您正在使用MVC ModelState,您可以将验证结果失败转换ModelState为如下条目(如果您在自定义模型绑定器中进行验证,这可能很有用):

var resultsGroupedByMembers = validationResults
    .SelectMany(vr => vr.MemberNames
                        .Select(mn => new { MemberName = mn ?? "", 
                                            Error = vr.ErrorMessage }))
    .GroupBy(x => x.MemberName);

foreach (var member in resultsGroupedByMembers)
{
    ModelState.AddModelError(
        member.Key,
        string.Join(". ", member.Select(m => m.Error)));
}
Run Code Online (Sandbox Code Playgroud)


gun*_*sus 5

我实现了一个用于验证的通用抽象类

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace App.Abstractions
{
    [Serializable]
    abstract public class AEntity
    {
        public int Id { get; set; }

        public IEnumerable<ValidationResult> Validate()
        {
            var vResults = new List<ValidationResult>();

            var vc = new ValidationContext(
                instance: this,
                serviceProvider: null,
                items: null);

            var isValid = Validator.TryValidateObject(
                instance: vc.ObjectInstance,
                validationContext: vc,
                validationResults: vResults,
                validateAllProperties: true);

            /*
            if (true)
            {
                yield return new ValidationResult("Custom Validation","A Property Name string (optional)");
            }
            */

            if (!isValid)
            {
                foreach (var validationResult in vResults)
                {
                    yield return validationResult;
                }
            }

            yield break;
        }


    }
}
Run Code Online (Sandbox Code Playgroud)