【问题标题】:Class/Model Level Validation (as opposed to Property Level)? (ASP.NET MVC 2.0)类/模型级别验证(相对于属性级别)? (ASP.NET MVC 2.0)
【发布时间】:2011-02-16 13:15:19
【问题描述】:

基本上就是标题所说的。我有几个属性组合在一起真正做出一个合乎逻辑的答案,我想运行一个服务器端验证代码(我编写的),它考虑到这些多个字段并只连接到一个验证输出/错误消息用户在网页上看到的。

我查看了 scott guthries 扩展属性并在您的 dataannotations 声明中使用它的方法,但是,正如我所见,没有办法在多个属性上声明 dataannotations 样式的属性,您只能放置声明(例如 [Email]、[Range]、[Required])超过一个属性 :(.

我已经查看了启动新项目时出现的默认 mvc 2.0 项目中的 PropertiesMustMatchAttribute,这个例子就像使用一对针检查你的机油一样有用 - 没用!

我已经尝试过这种方法,但是,创建了一个类级别的属性,但不知道如何在我的 aspx 页面中显示错误。我已经尝试过 html.ValidationMessage("ClassNameWhereAttributeIsAdded") 和其他各种东西,但它没有奏效。我应该提一下,没有一篇关于在此级别进行验证的博客文章 - 尽管这是任何项目或业务逻辑场景中的常见需求!

任何人都可以帮助我在我的 aspx 页面中显示我的消息,如果可能的话,还有一个适当的文档或参考解释这个级别的验证?

【问题讨论】:

  • 为什么这个问题被否决了?请在投反对票时发表评论。
  • 我同意,为什么这个问题会被否决?它提出了一个文档记录严重不足的问题......并且确实存在的文档只解决了问题的一部分,没有关于如何显示验证错误的内容。因为,由于 Darin 的建议,我选择不使用 fluent 验证(可在 codeplex 上获得)解决了我的所有问题。

标签: c# .net asp.net asp.net-mvc vb.net


【解决方案1】:

我已经使用 FluentValidator 完成了以下操作,如果它对任何人有用,感谢 Darin 的建议:

Public Class tProductValidator
Inherits AbstractValidator(Of tProduct)


Public Sub New()
    Const RetailWholsaleError As String = "You need to enter either a Retail Price (final price to user) or a Wholesale Price (price sold to us), but not both."
    Dim CustomValidation As System.Func(Of tProduct, ValidationFailure) =
     Function(x)
         If (x.RetailPrice Is Nothing AndAlso x.WholesalePrice Is Nothing) OrElse (x.RetailPrice IsNot Nothing AndAlso x.WholesalePrice IsNot Nothing) Then
             Return New ValidationFailure("RetailPrice", RetailWholsaleError)
         End If
         Return Nothing
     End Function

    Custom(CustomValidation)
End Sub

End Class

像往常一样,我被愚弄了,以为 MVC 是一个完整的框架,其中现实世界的场景是可能的。他们到底是如何发布这些东西的,甚至没有提到它的局限性,我不明白,然后我们的开发人员遇到了一个噩梦般的问题,或者我们不得不依靠 3rd 方的东西来做本质上 MVC 责任的事情,如果第 3 方物品不可用 - 那怎么办?

这也不是我在 mvc 2.0 中发现的第一个严重的回退,这个列表还在不断增加。

确保团队不断提供这些“酷”示例,说明事情是多么简单,使用虚构的非现实世界示例,而当涉及到真实的东西时,就像“哎呀,我们不知道也不关心,我们也不会告诉你它,你只需要遇到它并自己解决”类型交易

哦,在一次往返中更新视图的多个部分仍然是不可能的,除非您使用 js hack 并将返回的 html 剪切并分配给不同的 div ......您甚至不能返回多个视图或至少本地更新页面的多个区域(在一次往返中),这真是令人难过。

也许当 MVC 达到 3.0 版时,它实际上可能是完整的,手指交叉,因为 dotnet 框架在 clr 3.5 和包括 linq/EF to SQL 之前实际上并不“完整”...

【讨论】:

  • 我不确定我是否理解您关于在一次往返中更新视图的多个部分的投诉。我假设您的意思是 ajax/部分视图? “切断返回的 html”不是“js hack”,我的意思是地狱,这就是 UpdatePanels 在 WebForms 中所做的。只有现在您才能够以您认为合适的方式自己实现它。
  • @rossisdead 我不得不在那个问题上不同意你的观点,一个人不应该求助于 js 来一次更新网页的多个部分,目前,这是唯一的方法mvc仅是如果您更新第一个更新面板,然后在更新时触发下一个更新面板或div的另一个更新,然后当更新时,使用它来触发您的第三部分的另一个更新,当然,使用js来这样做,但用户端的延迟必须等待所有这些是错误且不可接受的,您应该能够返回多个视图并让它们更新所需的部分。
  • @rossisdead 也是,mvc 期货现在有这个多视图的东西,scottgu 在他的博客上告诉我,但我不知道它在期货 dll 中的什么位置或如何使用它,没有教程关于它或我所知道的任何事情。因此,如果网页的两个部分彼此位于不同的区域,您可以将其全部包裹在一个更新面板或 div 周围,但如果您在所有您都返回整个页面时,它会破坏拥有更新面板或使用 ajax 的目的希望它更新两个小 div。这里有人碰巧使用期货 dll 中的多视图返回功能,甚至见过它吗?
  • 我想我误解了你原来关于切割 html 的说法。我不建议您返回整个页面并解析出您需要的内容。你要做的是有一个动作来执行几个局部视图并只返回那些,然后有一些脚本来确定每个局部视图要更新页面的哪些部分。
  • @rossisdead 是的,你是对的,你必须在 JS 中编写一个脚本才能做到这一点,并且不再能够真正使用更新面板,并且需要这样做全部手动。如果我们能够以一种我们不必这样做的方式使用更新面板会很好,主要是因为它会扼杀整个 MVC 模式,并且您会发现自己无法遵守由其他方式提供的有利设计原则和模式MVC,对我来说真的很遗憾,并没有真正为不得不编写 JS 而烦恼,更何况打破了我来到 MVC 的设计/视图模式。
【解决方案2】:

我知道这已经得到解答,但如果您想继续使用 DataAnotations 属性,MVC3 似乎添加了另一种方法来完成此操作。

您的类可以实现提供 Validate 方法的 IValidatableObject 接口。只是另一种给猫剥皮的方法。

【讨论】:

    【解决方案3】:

    当通过 ValidationAttribute 的子类创建类级别属性时,如果验证失败,则 ModelState => 中没有相应的键 => 它将是一个空字符串,但在下面提供的链接中提供了解决方法,这将帮助 U 显示您的视图中的错误消息 通过只使用 html.ValidationMessage("urpropertyname") 而不是你尝试过的 html.ValidationMessage("ClassNameWhereAttributeIsAdded") 。

    Unable to set membernames from custom validation attribute in MVC2

    【讨论】:

      【解决方案4】:

      既然您已经查看了数据注释并得出结论,它们不适合您的场景,我建议您查看 FluentValidation,它的 integration with ASP.NET MVCunit test your validation logic 的方式 - 你赢了不要失望(我真的不反对数据注释,它们非常适合用于博客文章和教程,但是一旦您面对现实世界的应用程序,您很快就会意识到限制)。


      更新:

      根据 cmets 部分的要求,这里有一个使用 FluentValidation 框架的示例,其中一个服务器端验证函数访问多个属性(请不要这样做,因为它很难看,但有更好的方法):

      class AuthInfo
      {
          public string Username { get; set; }
          public string Password { get; set; }
          public string ConfirmPassword { get; set; }
      }
      
      class AuthInfoValidator : AbstractValidator<AuthInfo>
      {
          public override ValidationResult Validate(AuthInfo instance)
          {
              var result = base.Validate(instance);
              if (string.IsNullOrEmpty(instance.Username))
              {
                  result.Errors.Add(new ValidationFailure("Username", "Username is required"));
              }
              if (string.IsNullOrEmpty(instance.Password))
              {
                  result.Errors.Add(new ValidationFailure("Password", "Password is required"));
              }
              if (string.IsNullOrEmpty(instance.ConfirmPassword))
              {
                  result.Errors.Add(new ValidationFailure("ConfirmPassword", "ConfirmPassword is required"));
              }
              if (instance.Password != instance.ConfirmPassword)
              {
                  result.Errors.Add(new ValidationFailure("ConfirmPassword", "Passwords must match"));
              }
              return result;
          }
      }
      

      更自然的方法如下(它也不受属性重命名的影响,因为它不包含魔法字符串):

      class AuthInfoValidator : AbstractValidator<AuthInfo>
      {
          public AuthInfoValidator()
          {
              RuleFor(x => x.Username)
                  .NotEmpty()
                  .WithMessage("Username is required");
      
              RuleFor(x => x.Password)
                  .NotEmpty()
                  .WithMessage("Password is required");
      
              RuleFor(x => x.ConfirmPassword)
                  .NotEmpty()
                  .WithMessage("ConfirmPassword is required");
      
              RuleFor(x => x.ConfirmPassword)
                  .Equal(x => x.Password)
                  .WithMessage("Passwords must match");
          }
      }
      

      【讨论】:

      • 我可以将它与 MVC 一起使用,这样如果我使用共享主机计划,我可以将 dll 上传到 bin/文件夹,它应该可以工作还是比这更复杂?我同意,MVC 中的许多事情似乎还没有完成。这是我要添加到许多列表中的另一个。真的很令人失望。
      • 老实说,我从来没有在共享主机环境中使用过这个库,也不能确定它是否会起作用,但值得一试。
      • 只是快速浏览一下代码,它看起来非常漂亮。你有/知道任何其他 MVC 必备的吗?如果是这样,请分享。
      • AutoMapper (automapper.codeplex.com) 和 Jimmy Bogard 的博客 (lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/…) 是必读的。
      • @Darin,刚刚安装了 FluentValidation,但它不包含关于我正在尝试做的事情的文档 - 通过使用/访问多个属性的服务器端验证功能。关于如何使用 fluentValidation 解决此问题的任何想法?因为它不在 FluentValidation 文档中。
      【解决方案5】:

      可能的欺骗:

      Writing a CompareTo DataAnnotation Attribute

      你的问题的答案应该在那里,至少它指向这篇博文:

      http://byatool.com/mvc/custom-data-annotations-with-mvc-how-to-check-multiple-properties-at-one-time/


      要显示错误消息,您必须使用:

      <%: Html.ValidationMessage("") %>
      

      发生的情况是因为您在类级别上进行验证,您获得了一个带有键的 ModelState,通常是属性名称,带有一个空字符串。

      【讨论】:

      • 这是我尝试执行上述操作时遇到的错误(将空字符串传递给验证消息:值不能为空或空。参数名称:字段名
      • @Erx_VB.NExT.Coder - 你确定吗?我正在运行 MVC 2,但此功能背后的代码几乎相同。我打开了一个新的 MVC 2 项目并将 添加到默认的 Index.aspx 中。没有错误。相关的 MVC 源文件 ValidationExtensions.cs 中也没有“fieldName”参数。
      • 我在自己的项目中遇到此错误 - 当我尝试在我的项目(不是 mvc 默认项目)中显示错误时,我收到了上述错误。从那时起,我决定使用 FluentValidation 并且它可以创造奇迹,它实际上对现实世界的场景进行了有效的验证,而不仅仅是简单的“让我们展示一个可爱的非现实世界的例子,让事情看起来很酷”MS 抽出的例子。 .. 无法相信 MVC 有多弱,即使在第 2 版中它还远未完成。感谢您的帮助,但我只是对 VS 团队让事情看起来一切皆有可能而感到恼火。
      【解决方案6】:

      我不确定,但你是不是无缘无故地拒绝投票我的答案/问题的人(或者我对 VB.NET 的看法)?

      无论如何,PropertiesMustMatchAttribute 只是在对象上使用特定属性值的一个很好的实现。如果您需要使用对象的多个字段来运行某些逻辑,您可以使用以下方法来执行此操作,类似于 PropertiesMustMatchAttribute 所做的。

      下面是ValidationAttribute 的主要部分,它访问对象属性以运行一些逻辑。

          public override bool IsValid(object value)
          {
              PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
      
              // Get the values of the properties we need. 
              // Alternatively, we don't need to hard code the property names,
              // and instead define them via the attribute constructor
              object prop1Value = properties.Find("Person", true).GetValue(value);
              object prop2Value = properties.Find("City", true).GetValue(value);
              object prop3Value = properties.Find("Country", true).GetValue(value);
      
              // We can cast the values we received to anything
              Person person = (Person)prop1value; 
              City city = (City)prop2value;
              Country country = (Country)prop3value;
      
              // Now we can manipulate the values, running any type of logic tests on them
              if(person.Name.Equals("Baddie") && city.ZIP == 123456)
              {
                  return country.name.Equals("Australia");
              }
              else
              {
                  return false;
              }         
          }
      

      PropertiesMustMatchAttribute 只是使用Reflection 来完成一项普通任务。我试图分解代码以使其更具可读性/更易于理解。

      【讨论】:

      • errm,没有朋友,但我最近刚刚评论了您针对同一特定主题发表的帖子。另外,我已经达到了你所展示的水平......但是感谢额外的代码和描述,因为这确实有助于我掌握当前未记录的区域......我想知道你是否知道如何显示视图中的验证错误消息?我尝试做 ValidatorMessage("ClassName") 和类似的事情,但都没有奏效。你是如何显示消息的?
      猜你喜欢
      • 2012-11-20
      • 1970-01-01
      • 2011-12-08
      • 1970-01-01
      • 2012-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-27
      相关资源
      最近更新 更多