如何告诉数据注释验证器也验证复杂的子属性?

2023-12-07

我可以在验证父对象时自动验证复杂的子对象并将结果包含在填充的结果中吗ICollection<ValidationResult>?

如果我运行以下代码:

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

namespace ConsoleApplication1
{
    public class Person
    {
        [Required]
        public string Name { get; set; }

        public Address Address { get; set; }
    }

    public class Address
    {
        [Required]
        public string Street { get; set; }

        [Required]
        public string City { get; set; }

        [Required]
        public string State { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person
            {
                Name = null,
                Address = new Address
                {
                    Street = "123 Any St",
                    City = "New York",
                    State = null
                }
            };

            var validationContext = new ValidationContext(person, null, null);
            var validationResults = new List<ValidationResult>();

            var isValid = Validator.TryValidateObject(person, validationContext, validationResults);

            Console.WriteLine(isValid);

            validationResults.ForEach(r => Console.WriteLine(r.ErrorMessage));

            Console.ReadKey(true);
        }
    }
}

我得到以下输出:

False
The Name field is required.

但我期待类似的事情:

False
The Name field is required.
The State field is required.


我为更好的子对象验证解决方案提供了赏金,但理想情况下没有得到任何接受者

  • 验证任意深度的子对象
  • 处理每个对象的多个错误
  • 正确识别子对象字段上的验证错误。

我仍然很惊讶该框架不支持这一点。


问题 - 模型活页夹订单

不幸的是,这是标准行为Validator.TryValidateObject which

不递归地验证对象的属性值

正如杰夫·汉德利 (Jeff Handley) 的文章中指出的使用验证器验证对象和属性,默认情况下,验证器将按顺序验证:

  1. 属性级属性
  2. 对象级属性
  3. 模型级实现IValidatableObject

问题是,每一步......

如果任何验证器无效,Validator.ValidateObject将中止验证并返回失败

问题 - 模型绑定器字段

另一个可能的问题是模型绑定器只会对其决定绑定的对象运行验证。例如,如果您不为模型上的复杂类型内的字段提供输入,则模型绑定器根本不需要检查这些属性,因为它没有调用这些对象的构造函数。根据 Brad Wilson 的精彩文章ASP.NET MVC 中的输入验证与模型验证:

我们不递归地“深入”Address 对象的原因是,表单中没有任何内容绑定Address 内部的任何值。

解决方案 - 在验证属性的同时验证对象

解决此问题的一种方法是将对象级验证转换为属性级验证,方法是向属性添加自定义验证属性,该属性将随对象本身的验证结果一起返回。

乔什·卡罗尔的文章使用数据注释的递归验证提供了一个这样的策略的实现(最初在这个问题)。如果我们想验证复杂类型(如地址),我们可以添加自定义ValidateObject属性的属性,因此它在第一步进行评估

public class Person {
  [Required]
  public String Name { get; set; }

  [Required, ValidateObject]
  public Address Address { get; set; }
}

您需要添加以下内容验证对象属性执行:

public class ValidateObjectAttribute: ValidationAttribute {
   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      var results = new List<ValidationResult>();
      var context = new ValidationContext(value, null, null);

      Validator.TryValidateObject(value, context, results, true);

      if (results.Count != 0) {
         var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", validationContext.DisplayName));
         results.ForEach(compositeResults.AddResult);

         return compositeResults;
      }

      return ValidationResult.Success;
   }
}

public class CompositeValidationResult: ValidationResult {
   private readonly List<ValidationResult> _results = new List<ValidationResult>();

   public IEnumerable<ValidationResult> Results {
      get {
         return _results;
      }
   }

   public CompositeValidationResult(string errorMessage) : base(errorMessage) {}
   public CompositeValidationResult(string errorMessage, IEnumerable<string> memberNames) : base(errorMessage, memberNames) {}
   protected CompositeValidationResult(ValidationResult validationResult) : base(validationResult) {}

   public void AddResult(ValidationResult validationResult) {
      _results.Add(validationResult);
   }
}

解决方案 - 在验证属性的同时验证模型

对于实现的对象IValidatableObject,当我们检查 ModelState 时,我们还可以在返回错误列表之前检查模型本身是否有效。我们可以通过调用添加任何我们想要的错误ModelState.AddModelError(field, error)。如指定如何强制 MVC 验证 IValidatableObject,我们可以这样做:

[HttpPost]
public ActionResult Create(Model model) {
    if (!ModelState.IsValid) {
        var errors = model.Validate(new ValidationContext(model, null, null));
        foreach (var error in errors)                                 
            foreach (var memberName in error.MemberNames)
                ModelState.AddModelError(memberName, error.ErrorMessage);

        return View(post);
    }
}

Also,如果您想要更优雅的解决方案,您可以通过在 Application_Start() 中提供您自己的自定义模型绑定器实现来编写一次代码ModelBinderProviders.BinderProviders.Add(new CustomModelBinderProvider());。有很好的实现here and here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何告诉数据注释验证器也验证复杂的子属性? 的相关文章

  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • 按成员序列化

    我已经实现了template
  • 使用实体框架模型输入安全密钥

    这是我今天的完美想法 Entity Framework 中的强类型 ID 动机 比较 ModelTypeA ID 和 ModelTypeB ID 总是 至少几乎 错误 为什么编译时不处理它 如果您使用每个请求示例 DbContext 那么很
  • 类模板参数推导 - clang 和 gcc 不同

    下面的代码使用 gcc 编译 但不使用 clang 编译 https godbolt org z ttqGuL template
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 不同枚举类型的范围和可转换性

    在什么条件下可以从一种枚举类型转换为另一种枚举类型 让我们考虑以下代码 include
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 如何序列化/反序列化自定义数据集

    我有一个 winforms 应用程序 它使用强类型的自定义数据集来保存数据进行处理 它由数据库中的数据填充 我有一个用户控件 它接受任何自定义数据集并在数据网格中显示内容 这用于测试和调试 为了使控件可重用 我将自定义数据集视为普通的 Sy
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • IEnumreable 动态和 lambda

    我想在 a 上使用 lambda 表达式IEnumerable
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • 如何将服务器服务连接到 Dynamics Online

    我正在修改内部管理应用程序以连接到我们的在线托管 Dynamics 2016 实例 根据一些在线教程 我一直在使用OrganizationServiceProxy out of Microsoft Xrm Sdk Client来自 SDK
  • C++ 中类级 new 删除运算符的线程安全

    我在我的一门课程中重新实现了新 删除运算符 现在我正在使我的代码成为多线程 并想了解这些运算符是否也需要线程安全 我在某处读到 Visual Studio 中默认的 new delete 运算符是线程安全的 但这对于我的类的自定义 new

随机推荐

  • JavaScript 如何分配函数的“name”属性?

    在 JavaScript 中 当我定义这样的函数时 function aaa 我稍后可以通过以下方式访问该名称name属性 aaa name 这将返回 aaa 但是 当我通过定义函数时var 从技术上讲它应该是匿名函数 没有name属性 v
  • Android布局和定位问题

    最近我正在构建一个应用程序 现在我在布局和定位方面遇到了一些问题 事实上 我构建了我的布局 但是当我在更大的屏幕上测试时 所有内容都崩溃了 并且我的应用程序的外观不好 制作 UI 的最佳方式是什么 参考支持多屏 另外 在创建 UI 时 尽量
  • pandas.to_datetime 给出 OutOfBoundsDatetime 错误

    我有某种格式的数据 我想将其读入 pandas DataFrame 中 有些行给我一个错误 下面是其中一个字符串的一个最小示例 但我有几个它不起作用的地方 奇怪的是有些它起作用的地方 确切的错误是 OutOfBoundsDatetime 越
  • 按 Enter 键确定是否从 HTML 5 数据列表中选择了元素

    jQuery document body on input icdCodeInput function event 我有一个带有 icdCodeInput 类的 HTML 5 数据列表 当我使用鼠标或按 Enter 键从列表中选择一个项目时
  • 是否可以使用加载项在 Visual Studio 2012 中处理 Qt4 项目?

    我发现 Qt VS addin 1 1 x 可与 Visual Studios 包括 2010 一起使用 Qt VS addin 1 2 x 不支持 VS 2012 中的 Qt4 该插件是否有任何可以在 VS 2012 中处理 Qt4 的分
  • 在 GROUP BY 中使用 LIMIT 来获得每组 N 个结果?

    以下查询 SELECT year id rate FROM h WHERE year BETWEEN 2000 AND 2009 AND id IN SELECT rid FROM table2 GROUP BY id year ORDER
  • PostgreSQL hstore 数组列上的索引

    我知道您可以在 hstore 列中的字段上创建索引 我知道您还可以在数组列上创建 GIN 索引 但是在 hstore 数组上创建索引的语法是什么 e g CREATE TABLE customer pk serial PRIMARY KEY
  • 防止数组覆盖并创建新的数组索引

    我有一个文件 我需要将该文件的内容保存在我的 MySQL 数据库中 这是我用来解析文件的代码 lines file tmp filename data array if handle fopen tmp filename r FALSE w
  • 将时间戳(以微秒为单位)转换为 r 中的数据和时间

    我正在尝试将以微秒为单位的时间戳转换为 R 中的以下格式 年 月 日 时 分 秒 我尝试过不同的方法 但未能成功 按照我的代码 options digits 16 value 1521222492687300 as POSIXct valu
  • 如何在 Perl 的 system() 中检查管道中第一个程序的状态?

    perl e system crontab1 l print 按预期返回 1 程序 crontab1 不存在 perl e system crontab1 l grep blah print 返回 256 检查第一个 或两个 程序的状态的方
  • 如何自动缩放传单中的多边形?

    我的 geoJson 格式如下 statesData features push type Feature id AFG properties name Afghanistan geometry type Polygon coordinat
  • 是否可以阻止 JIT 优化方法调用?

    我们正在构建一个用于 Java 字节码程序的平均情况运行时分析的工具 其中一部分是测量实际运行时间 因此 我们将采用任意的 用户提供的方法 该方法可能有也可能没有结果 并且可能有也可能没有副作用 示例包括快速排序 阶乘 虚拟嵌套循环 并执行
  • 使用哈希值跟踪文件的唯一版本

    我将跟踪可能数百万个不同文件的不同版本 我的目的是对它们进行散列以确定我已经看到了该文件的特定版本 目前 我只使用 MD5 该产品仍在开发中 因此尚未处理过数百万个文件 这显然不够长 无法避免冲突 然而 这是我的问题 如果我使用两种不同的方
  • ffmpeg AVFrame 获取完整解码数据到 char*

    我在循环中获取帧并使用 ffmpeg 对其进行解码 得到 AVFrame 作为其结果 因此 我必须将帧的必要像素数据获取到 char 中 并作为回调函数的参数给出 那么如何生成这样的 char 数组呢 在互联网上我看到了一些例子 例如 fo
  • 每小时自动运行一次 php

    我使用的是共享 Windows 主机 其中允许通过 php 代码发送 120 封邮件 小时 我有一个 php 页面可以一次发送超过 200 封邮件 但我想每小时运行一次该页面 计划任务 我将拆分电子邮件 以 100 秒为单位 并希望每小时自
  • amp-iframe 内的 amp 页面上的 Disqus

    我尝试在 amp 文档上实现 Disqus 我的想法是使用amp iframe它加载一个仅包含 Disqus 的小文档 我用的是这个放大器框架
  • api.get_retweeter_ids() 实际上是如何工作的(Tweepy Python)?

    我对 twitter api 很陌生 我一直在尝试获取转发特定推文的每个人的 ID 列表 经过几次尝试后 我无法使用 api get retweeter ids 来获取每个 id 似乎总是能得到一些 我知道每个请求的限制为 100 个 但在
  • Javascript:如何避免在函数中添加新属性?

    我是一个JS新手 正在看书JavaScript 模式为了理解 我可以看到的代码片段之一 var myFunc function param myFunc cache 这表明函数体之外的任何人都可以添加新属性 这不会破坏封装吗 如果程序的其他
  • Delphi Firemonkey 跨平台 - 传递 Windows 句柄的通用方法

    我正沉浸在我的第二个适用于 Windows 和 OSX 的 Firemonkey 应用程序中 并慢慢地转换我的函数库以处理跨平台问题 我正在尝试创建一个通用的 SelectDirectory 函数 它将运行 Windows 或 OSX 平台
  • 如何告诉数据注释验证器也验证复杂的子属性?

    我可以在验证父对象时自动验证复杂的子对象并将结果包含在填充的结果中吗ICollection