2010-03-10 92 views
111

如何使用数据注释对模型进行条件验证?ASP.NET MVC条件验证

例如,让我们说我们有以下的模型(人,高级):

public class Person 
{ 
    [Required(ErrorMessage = "*")] 
    public string Name 
    { 
     get; 
     set; 
    } 

    public bool IsSenior 
    { 
     get; 
     set; 
    } 

    public Senior Senior 
    { 
     get; 
     set; 
    } 
} 

public class Senior 
{ 
    [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value 
    public string Description 
    { 
     get; 
     set; 
    } 
} 

而以下几种观点:

<%= Html.EditorFor(m => m.Name)%> 
<%= Html.ValidationMessageFor(m => m.Name)%> 

<%= Html.CheckBoxFor(m => m.IsSenior)%> 
<%= Html.ValidationMessageFor(m => m.IsSenior)%> 

<%= Html.CheckBoxFor(m => m.Senior.Description)%> 
<%= Html.ValidationMessageFor(m => m.Senior.Description)%> 

我想是 “Senior.Description”属性条件必需字段基于选择“IsSenior”propery(true - > required)。如何在ASP.NET MVC 2中使用数据注释实现条件验证?

+1

我最近问过类似的问题:http://stackoverflow.com/questions/2280539/custom-model-validation-of-dependent-properties-using-data-annotations – 2010-03-10 13:44:09

+0

我很困惑。一个“高级”对象总是高级的,那么为什么IsSenior在这种情况下是错误的。当Person.IsSenior为false时,您不需要'Person.Senior'属性为null。或者为什么不按如下方式实现'IsSenior'属性:'bool IsSenior {get {return this.Senior!= null; }}。 – Steven 2010-03-10 13:46:37

+0

Steven:“IsSenior”转换为视图中的复选框字段。当用户选中“IsSenior”复选框时,“Senior.Description”字段变为强制。 – 2010-03-10 14:47:14

回答

58

我已经处理"ModelState"字典,它由控制器包含解决了这个。 ModelState字典包含所有必须验证的成员。

这里是解决方案:

如果你需要基于一些领域(例如,如果A = true,则需要B),同时保持性能级别的错误消息(此实现条件验证对于处于对象级别的自定义验证器不是这样),您可以通过简单地从其中删除不需要的验证来处理“ModelState”来实现此目的。

...在某些类...

public bool PropertyThatRequiredAnotherFieldToBeFilled 
{ 
    get; 
    set; 
} 

[Required(ErrorMessage = "*")] 
public string DepentedProperty 
{ 
    get; 
    set; 
} 

...类继续...

...在某些控制器的动作......

if (!PropertyThatRequiredAnotherFieldToBeFilled) 
{ 
    this.ModelState.Remove("DepentedProperty"); 
} 

。 ..

有了这个,我们实现了条件验证,同时保留了其他条件。


UPDATE:

这是我最后的实现:我已经使用的模型和验证它实现上述接口的模式操作属性的接口。接口规定了Validate(ModelStateDictionary modelState)方法。 action上的属性只是调用IValidatorSomething上的Validate(modelState)。

我不想让这个答案复杂化,所以我没有提到最终的实现细节(最后,它是生产代码中的问题)。

+13

缺点是您的验证逻辑的一部分位于模型中,另一部分位于控制器中。 – 2010-03-11 12:45:19

+0

当然这不是必须的。我只是展示最基本的例子。我已经实现了这个接口的模型和动作属性,验证模型,实现提到的接口。接口出现Validate(ModelStateDictionary modelState)方法。所以最后你在模型中做所有的验证。无论如何,好点。 – 2010-03-11 16:49:58

+0

我很喜欢这种方法的简单性,直到MVC团队开发出更好的东西。但是你的解决方案是否可以在客户端验证启 – Aaron 2011-02-21 01:58:35

3

您需要在人员级别进行验证,而不是在高级级别,或者高级人员必须对其父级人员进行参考。在我看来,你需要一个自我验证机制来定义Person上的验证,而不是它的某个属性。我不确定,但我不认为DataAnnotations支持这个开箱即用的功能。你可以做什么创建你自己的Attribute,它源于ValidationAttribute,它可以在类级别进行修饰,然后创建一个自定义验证器,该验证器还允许这些类级验证器运行。

我知道验证应用程序块支持开箱即用的自我验证,但VAB的学习曲线非常陡峭。然而,这是一个使用VAB一个例子:

[HasSelfValidation] 
public class Person 
{ 
    public string Name { get; set; } 
    public bool IsSenior { get; set; } 
    public Senior Senior { get; set; } 

    [SelfValidation] 
    public void ValidateRange(ValidationResults results) 
    { 
     if (this.IsSenior && this.Senior != null && 
      string.IsNullOrEmpty(this.Senior.Description)) 
     { 
      results.AddResult(new ValidationResult(
       "A senior description is required", 
       this, "", "", null)); 
     } 
    } 
} 
+0

“您需要在人员级别进行验证,而不是在高级级别”是的,这是一个选项,但是您失去了将错误附加到特定字段的能力,这是高级对象所要求的。 – 2010-03-11 08:34:28

16

您可以通过删除的ModelState错误有条件地禁用验证:

ModelState["DependentProperty"].Errors.Clear(); 
132

有一个更好的方法来在MVC3添加条件验证规则。有你的模型继承IValidatableObject并实现验证方法:在http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

+2

+1,实现IValidateObject真棒! :)但是,你的括号几乎没有关闭(我编辑它们来修复它)。 – 2012-04-17 22:53:45

+0

如果属性是“int”类型,需要值,如果填充该字段,则验证不起作用。 – 2012-12-06 12:45:28

+2

不幸的是,Microsoft把这个放在了错误的层 - 验证是业务逻辑,而且这个接口在System.Web DLL中。为了使用它,您必须为业务层提供对演示技术的依赖。 – NightOwl888 2013-07-28 15:45:14

31

public class Person : IValidatableObject 
{ 
    public string Name { get; set; } 
    public bool IsSenior { get; set; } 
    public Senior Senior { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     if (IsSenior && string.IsNullOrEmpty(Senior.Description)) 
      yield return new ValidationResult("Description must be supplied."); 
    } 
} 

看到更多的是说明我昨天有同样的问题,但我做到了,其中既适用于客户端的一个非常干净的方式和服务器端验证。

条件:基于模型中其他属性的值,您需要创建另一个属性。下面是代码

public class RequiredIfAttribute : RequiredAttribute 
{ 
    private String PropertyName { get; set; } 
    private Object DesiredValue { get; set; } 

    public RequiredIfAttribute(String propertyName, Object desiredvalue) 
    { 
     PropertyName = propertyName; 
     DesiredValue = desiredvalue; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext context) 
    { 
     Object instance = context.ObjectInstance; 
     Type type = instance.GetType(); 
     Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null); 
     if (proprtyvalue.ToString() == DesiredValue.ToString()) 
     { 
      ValidationResult result = base.IsValid(value, context); 
      return result; 
     } 
     return ValidationResult.Success; 
    } 
} 

这里属性名是指您想使你的病情 DesiredValue是,你的其他属性有属性名(财产)的具体数值要验证的属性时需要

说你有以下

public class User 
{ 
    public UserType UserType { get; set; } 

    [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))] 
    public string Password 
    { 
     get; 
     set; 
    } 
} 

最后但并非最不重要,注册适配器为您的属性,以便它可以做客户端验证(我把它在Global.asax中,的Application_Start)

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter)); 
+0

这是原始起点http://miroprocessordev.blogspot.com/2012/08/aspnet- mvc-conditional-validation-using.html – 2013-04-13 17:00:33

+0

在asp.net mvc2中是否有任何等价的解决方案? ValidationResult,ValidationContext类在asp.net中不可用mvc2(.net framework 3.5) – 2013-11-08 07:45:34

+0

@User_MVC我没有在mvc2上工作,不知道但是如果有任何验证,请尝试扩展它 – 2013-12-04 23:04:49

6

现在有做这个条件的验证(其他方便的数据注释验证中)的框架开箱: http://foolproof.codeplex.com/

具体来说,看一看的[RequiredIfTrue(“IsSenior”) ] 验证器。你直接把它放在你想要验证的属性上,这样你就可以得到与“高级”属性相关联的验证错误所需的行为。

它可作为NuGet包使用。

3

我有同样的问题,需要修改[Required]属性 - 使得依赖于http请求的字段成为必需。解决方案类似于Dan Hunex的答案,但是他的解决方案无法正常工作(请参阅注释)。我不使用不显眼的验证,只是MicrosoftMvcValidation.js开箱即用。 这是。实现您的自定义属性:

public class RequiredIfAttribute : RequiredAttribute 
{ 

    public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/) 
    { 

    } 

    protected override ValidationResult IsValid(object value, ValidationContext context) 
    { 

    //You can put your logic here 

     return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need 
    } 


} 

然后,你需要实现自定义提供商使用它作为一个适配器在Global.asax的

public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute> 
{ 

    ControllerContext ccontext; 
    public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute) 
     : base(metadata, context, attribute) 
    { 
     ccontext = context;// I need only http request 
    } 

//override it for custom client-side validation 
    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() 
    {  
       //here you can customize it as you want 
     ModelClientValidationRule rule = new ModelClientValidationRule() 
     { 
      ErrorMessage = ErrorMessage, 
    //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"  
      ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none"; 
     }; 
     return new ModelClientValidationRule[] { rule }; 
     } 
} 

并通过订单修改您的Global.asax

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator)); 

这里是

[RequiredIf] 
public string NomenclatureId { get; set; } 

对我来说,最主要的优势是我不必为不引人注目的验证代码定制客户端验证器。它的工作原理与[必需]一样,但仅限于您需要的情况。

+0

关于扩展'DataAnnotationsModelValidator'的部分正是我需要看到的。谢谢。 – twip 2016-01-27 19:08:07

0

从模型状态条件去除错误的典型用法:

  1. 使控制器动作
  2. 有条件的第一部分进行逻辑从ModelState中
  3. 执行现有逻辑的其余部分去除错误(通常模型状态验证,那么一切)

例子:

public ActionResult MyAction(MyViewModel vm) 
{ 
    // perform conditional test 
    // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey") 

    // Do typical model state validation, inside following if: 
    //  if (!ModelState.IsValid) 

    // Do rest of logic (e.g. fetching, saving 

在您的示例中,保持所有内容均为原样,并将建议的逻辑添加到Controller的操作中。我假设传递给控制器​​操作的ViewModel具有Person和Senior Person对象,并在UI中填充了其中的数据。

24

我一直在使用这个惊人的NuGet,做动态注释ExpressiveAnnotations

你可以验证你能想到的任何逻辑:

public string Email { get; set; } 
public string Phone { get; set; } 
[RequiredIf("Email != null")] 
[RequiredIf("Phone != null")] 
[AssertThat("AgreeToContact == true")] 
public bool? AgreeToContact { get; set; } 
+3

ExpressiveAnnotation库是这里所有答案的最灵活和最通用的解决方案。感谢分享! – dotnetguy 2016-09-05 08:00:58

+2

我一直在砸我的头,试图找到一个坚实的一天的解决方案。 ExpressiveAnnotations看起来是我的修补程序! – Caverman 2017-05-03 20:22:16

+0

表达式注释库非常棒! – 2017-08-05 15:43:50

0

我使用MVC 5,但你可以尝试像这个:

public DateTime JobStart { get; set; } 

[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")] 
[DisplayName("Start Date")] 
[Required] 
public DateTime? StartDate { get; set; } 

在你的情况下,你会说“IsSenior == true”之类的东西。 然后您只需检查您的发布操作的验证。