简体   繁体   English

验证C#中的列表

[英]Validating lists in C#

We're using DataAnnotations to validate our model. 我们正在使用DataAnnotations来验证我们的模型。

A very simplified version of our model is: 我们模型的一个非常简化的版本是:

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

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

Now, the funny thing is that if I create a Thing with no name and add it to the model, I would expect validation to fail, but it passes (shock horror!). 现在,有趣的是,如果我创建一个没有名称的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!

I think the reason for this is that when you validate the model, it validates each property but not items in the property if it's a collection. 认为这样做的原因是,当您验证模型时,它会验证每个属性,但会验证属性中的项目(如果是集合)。 Things is a property that has no validation, so it passes (despite the fact that it contains invalid item). Things是没有验证的属性,因此它可以通过(尽管它包含无效项)。

How can we ensure that validation also validates items in collection properties? 我们如何确保验证也可以验证集合属性中的项目? Is there some out-of-the-box validator I could use? 我可以使用一些现成的验证器吗?

I have fixed this by creating a custom validator for collections that checks validation on each item. 我已经通过为集合创建一个自定义验证器来解决此问题,该集合用于检查每个项目的验证。 A simplified code would look like this: 简化的代码如下所示:

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
}

Now decorating the model this way would work as expected: 现在以这种方式装饰模型将按预期工作:

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

Another alternative, if this is ASP.NET MVC , would be to implement IValidatableObject in your model. 如果这是ASP.NET MVC ,则另一种选择是在模型中实现IValidatableObject Like: 喜欢:

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

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

Then the result of ModelState.IsValid in your controller will depend on that implementation of Validate method. 然后, controllerModelState.IsValid的结果将取决于Validate方法的实现。 This is useful when multiple properties of your model are dependent on each other. 当模型的多个属性相互依赖时,这很有用。

The default behavior in your question is not surprising, let's describe it. 您问题中的默认行为不足为奇,下面将对其进行描述。 Let's say you have a property of type Dictionary<string, Thing> or a property of type Something<Thing> or an untyped collection containing Thing objects in the Model , then how could we expect the Validator perform a validation against Thing objects which are stored in those properties? 假设您有一个Dictionary<string, Thing>类型的属性或Something<Thing>类型的属性或Model包含Thing对象的无类型集合,那么我们如何期望Validator对存储的Thing对象执行验证在那些属性?

We cannot expect Validator to perform a validation against Thing objects which are stored in those properties, because it doesn't have any information about how it should validate those properties. 我们不能期望Validator对存储在这些属性中的Thing对象执行验证,因为它没有有关如何验证那些属性的任何信息。 To validate a property, Validator looks for ValidationAttribute for that property and since it doesn't find any validation attribute, it doesn't validate that property. 为了验证属性, Validator为该属性寻找ValidationAttribute ,并且由于找不到任何验证属性,因此不会验证该属性。

As a result, you need to create some ValidationAttribute to do that for you and decorate properties with validation attributes. 结果,您需要创建一些ValidationAttribute来为您执行此操作,并用验证属性装饰属性。 You can implement something like what you implemented in your answer. 您可以实现与答案中类似的内容。

Note: In context of ASP.NET MVC you don't need to be worried about it. 注意:在ASP.NET MVC的上下文中,您不必担心它。 Default model binder takes care of all validation attributes when model binding, even when model binding to a list. 当模型绑定时,即使模型绑定到列表,默认模型绑定器也会照顾所有验证属性。

It's what the default model binder does. 这是默认模型联编程序的工作。 When creating each element, when assigning values to properties, it validates property and add the validation errors to model state. 在创建每个元素时,在为属性分配值时,它会验证属性并将验证错误添加到模型状态。 At last, all properties and objects are validated and model state contains all validation errors. 最后,所有属性和对象都经过验证,并且模型状态包含所有验证错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM