【问题标题】:Why doesn't automatic attribute validation work for models inheriting collections?为什么自动属性验证不适用于继承集合的模型?
【发布时间】:2020-12-13 10:26:04
【问题描述】:

我想通过属性向 Web API (.NET Framework 4.7.2) 添加模型验证,但我发现如果模型是集合,这将不起作用。

我有一个从字典继承的模型类。例如:

...
using System.ComponentModel.DataAnnotations;
...

public class MyModel : Dictionary<string, string>
{
    [StringLength(100)]
    public string SomeField
    {
        get => TryGetValue("someField", out var value) ? value : null;
        set => this["someField"] = value;
    }
}

我做了什么:

  1. [ApiController] 属性添加到控制器类以激活请求的自动模型验证。
  2. 在项目中添加了System.ComponentModel.DataAnnotations
  3. 向模型字段添加了验证属性。

这适用于简单的模型类,但如果模型是从集合继承的,则不会发生属性验证。

为了解决这个问题,我实现了IValidatableObject接口,我在其中写了以下内容:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    const string validationStatusFieldName = "alreadyValidated";
    if (validationContext.Items.TryGetValue(validationStatusFieldName, out var alreadyValidated) && (bool) alreadyValidated)
        return Enumerable.Empty<ValidationResult>();
    validationContext.Items[validationStatusFieldName] = true;

    var validationResults = new List<ValidationResult>();
    var isValid = Validator.TryValidateObject(validationContext.ObjectInstance, validationContext, validationResults, true);
    return isValid
        ? Enumerable.Empty<ValidationResult>()
        : validationResults;
}

在这种情况下,Validate 方法会被自动调用,我们可以在其中手动验证属性。但是,如果属性验证通过且没有验证错误,则将调用 Validate 方法,在这种情况下,该方法将一遍又一遍地调用自身,从而导致堆栈溢出异常。所以我添加了一个检查以确保验证已经发生。

这样就可以了,但是不清楚:如果这个模型继承了一个集合,为什么不自动检查属性,如何解决?

【问题讨论】:

  • 为什么需要将模型继承到集合中。您可以在模型内部使用集合类型Property 的简单模型中执行此操作。
  • 它是否可以被继承取决于实际的属性:自定义属性声明以System.AttributeUsageAttribute 开头,它定义了属性类的一些关键特征。例如,您可以指定您的属性是否可以被其他类继承或指定该属性可以应用于哪些元素。 也许这些属性不是以允许它们被继承的方式创建的。
  • 继承一个集合会带来很多通常最好避免的奇怪现象。支持composition over inheritance。例如,MyModel 可能有一个 Dictionary 属性,其处理方式与其固有属性不同(如SomeField)。
  • 是的,没错。但我想知道为什么它会这样工作?

标签: c# validation asp.net-web-api data-annotations


【解决方案1】:

验证器有一个condition,他可以在其中准确选择验证对象的方式。首先检查模型是一个集合,然后检查它的每个元素。并且由于该集合存储了没有验证约束的字符串,因此验证无需任何 cmets。而如果通过属性验证字段没有问题,那么会调用Validate方法,就是这样。

如何解决这个问题:

  • 实现IValidatableObject接口,在Validate方法中,尝试验证当前实例,记住退出无限递归。 (已在问题中完成)

  • 为模型类创建一个自定义验证属性(继承自ValidationAttribute),它将搜索具有验证属性的属性并独立验证它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-29
    • 2014-09-21
    • 2014-08-25
    • 1970-01-01
    • 2012-04-22
    • 2011-04-15
    • 2016-10-13
    • 2010-09-07
    相关资源
    最近更新 更多