在 C# 中验证列表

Validating lists in C#

我们正在使用 DataAnnotations 来验证我们的模型。

我们模型的一个非常简化的版本是:

public class Model
{
    public List<Thing> Things;
}

public class Thing
{
    [Required]
    public string Name {get;set;}
}

现在,有趣的是,如果我创建一个没有名称的 Thing 并将其添加到模型中,我预计验证会失败,但它通过了(震惊恐怖!)。

var model = new Model ();
var invalidThing = new Thing (); // No name would fail validation
model.Things.Add(invalidThing );

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

Assert.False (isValid);  // This fails!

认为 这样做的原因是当您验证模型时,它会验证每个 属性 但不会验证 属性 中的项目,如果它是collection。 Things 是一个没有验证的 属性,所以它通过了(尽管它包含无效的项目)。

我们如何确保验证也验证 collection 属性中的项目?我可以使用一些 out-of-the-box 验证器吗?

我已通过为集合创建自定义验证器来检查每个项目的验证来解决此问题。简化的代码如下所示:

public class ValidateEachItemAttribute : ValidationAttribute
{
    protected readonly List<ValidationResult> validationResults = new List<ValidationResult>();

    public override bool IsValid(object value)
    {
        var list = value as IEnumerable;
        if (list == null) return true;

        var isValid = true;

        foreach (var item in list)
        {
            var validationContext = new ValidationContext(item);
            var isItemValid = Validator.TryValidateObject(item, validationContext, validationResults, true);
            isValid &= isItemValid;
        }
        return isValid;
    }

    // I have ommitted error message formatting
}

现在以这种方式装饰模型将按预期工作:

public class Model
{
    [ValidateEachItem]
    public List<Thing> Things;
}

如果这是 ASP.NET MVC,另一种选择是在您的模型中实施 IValidatableObject。喜欢:

public class Model: IValidatableObject
{
    public List<Thing> Things;

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
       //your validation logic here
    }
}

那么 controllerModelState.IsValid 的结果将取决于 Validate 方法的实施。当模型的多个属性相互依赖时,这很有用。

你问题中的默认行为并不奇怪,让我们来描述一下。 假设您有一个 Dictionary<string, Thing> 类型的 属性 或 Something<Thing> 类型的 属性 或 Model 中包含 Thing 个对象的无类型集合,那么我们怎么能期望 Validator 对存储在这些属性中的 Thing 对象执行验证呢?

我们不能指望 Validator 对存储在这些属性中的 Thing 对象执行验证,因为它没有关于应如何验证这些属性的任何信息。要验证 属性,Validator 会为 属性 查找 ValidationAttribute,因为它没有找到任何验证属性,所以它不会验证 属性 .

因此,您需要创建一些 ValidationAttribute 来为您执行此操作并使用验证属性装饰属性。您可以实施类似于您在答案中实施的内容。

注意: 在 ASP.NET MVC 的上下文中,您无需担心它。默认模型绑定器在模型绑定时处理所有验证属性,即使在模型绑定到列表时也是如此。

这是默认模型活页夹的作用。创建每个元素时,在为属性赋值时,它会验证 属性 并将验证错误添加到模型状态。最后,所有属性和对象都经过验证,模型状态包含所有验证错误。