RequredIf 条件验证属性

我正在寻找一些关于实现验证属性的最佳方法的建议,该属性可以执行以下操作。

模特

public class MyInputModel
{
[Required]
public int Id {get;set;}


public string MyProperty1 {get;set;}
public string MyProperty2 {get;set;}
public bool MyProperty3 {get;set;}


}

我希望至少有一个 prop1 prop2 prop3的值,如果 prop3是唯一的值填充它应该不等于 false。 如何为此编写验证属性?

谢谢你的帮助!

134280 次浏览

我昨天也遇到了同样的问题,但是我用一种非常干净的方式解决了这个问题,这种方式既适用于客户端验证,也适用于服务器端验证。

条件 : 根据模型中其他属性的值,您希望创建所需的另一个属性。密码如下:

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;
}
}

PropertyName 是您希望在其上创建条件的属性
DesiredValue 是 PropertyName (property)的特定值,必须对其他属性进行必要的验证

假设你有以下信息:

public enum UserType
{
Admin,
Regular
}


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));

编辑

有些人报告了客户端无论如何都会开火或者不工作的问题。所以我修改了上面的代码,使用 Javascript 进行有条件的客户端验证。对于这种情况,您不需要注册适配器

 public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
private readonly RequiredAttribute _innerAttribute;


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


protected override ValidationResult IsValid(object value, ValidationContext context)
{
var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);


if (dependentValue.ToString() == DesiredValue.ToString())
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
}
}
return ValidationResult.Success;
}


public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredif",
};
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;


yield return rule;
}
}

最后是 javascript (绑定它并渲染它... ... 将它放在自己的脚本文件中)

$.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options) {
options.rules['requiredif'] = options.params;
options.messages['requiredif'] = options.message;
});


$.validator.addMethod('requiredif', function (value, element, parameters) {
var desiredvalue = parameters.desiredvalue;
desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
var actualvalue = {}
if (controlType == "checkbox" || controlType == "radio") {
var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
actualvalue = control.val();
} else {
actualvalue = $("#" + parameters.dependentproperty).val();
}
if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
var isValid = $.validator.methods.required.call(this, value, element, parameters);
return isValid;
}
return true;
});

显然,您需要包含不引人注目的验证 jQuery 作为需求

我知道这个问题已经被问过一段时间了,但是最近我遇到了类似的问题并且找到了另外一个,但是在我看来这是一个更加完整的解决方案。我决定实现一种机制,它提供条件属性,根据逻辑表达式中定义的其他属性值和它们之间的关系来计算验证结果。

使用它,你可以通过以下方式达到你要求的结果:

[RequiredIf("MyProperty2 == null && MyProperty3 == false")]
public string MyProperty1 { get; set; }


[RequiredIf("MyProperty1 == null && MyProperty3 == false")]
public string MyProperty2 { get; set; }


[AssertThat("MyProperty1 != null || MyProperty2 != null || MyProperty3 == true")]
public bool MyProperty3 { get; set; }

有关 Expressive批注库 可以在这里找到的详细信息。它应该简化许多声明性验证案例,而无需编写额外的特定于案例的属性或在控制器内使用命令式验证方法。

如果尝试使用“ ModelState.Remove”或“ ModelState [“ Prop”] . Errors.Clear ()”,则“ ModelState.IsValid”仍将返回 false。

为什么不直接从模型中删除默认的“必需”注释,并在 Controller‘ Post’操作上的“ ModelState.IsValid”之前进行自定义验证呢?像这样:

if (!String.IsNullOrEmpty(yourClass.Property1) && String.IsNullOrEmpty(yourClass.dependantProperty))
ModelState.AddModelError("dependantProperty", "It´s necessary to select some 'dependant'.");

这里与其他解决方案的主要区别在于,这个解决方案在服务器端重用 RequiredAttribute中的逻辑,并在客户端使用 required的验证方法 depends属性:

public class RequiredIf : RequiredAttribute, IClientValidatable
{
public string OtherProperty { get; private set; }
public object OtherPropertyValue { get; private set; }


public RequiredIf(string otherProperty, object otherPropertyValue)
{
OtherProperty = otherProperty;
OtherPropertyValue = otherPropertyValue;
}


protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
PropertyInfo otherPropertyInfo = validationContext.ObjectType.GetProperty(OtherProperty);
if (otherPropertyInfo == null)
{
return new ValidationResult($"Unknown property {OtherProperty}");
}


object otherValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (Equals(OtherPropertyValue, otherValue)) // if other property has the configured value
return base.IsValid(value, validationContext);


return null;
}


public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "requiredif"; // data-val-requiredif
rule.ValidationParameters.Add("other", OtherProperty); // data-val-requiredif-other
rule.ValidationParameters.Add("otherval", OtherPropertyValue); // data-val-requiredif-otherval


yield return rule;
}
}


$.validator.unobtrusive.adapters.add("requiredif", ["other", "otherval"], function (options) {
var value = {
depends: function () {
var element = $(options.form).find(":input[name='" + options.params.other + "']")[0];
return element && $(element).val() == options.params.otherval;
}
}
options.rules["required"] = value;
options.messages["required"] = options.message;
});

我让它在 ASP.NET MVC 5上工作

我看到很多人对这个代码感兴趣并且深受其害,我知道这真的是第一次让人感到困惑和不安。

笔记

  • 在服务器端和客户端都在 MVC 5上工作: D
  • 我根本没有安装“ ExpressiveAnnotations”库。
  • 我正在破解 @ Dan Hunex的原始代码,在上面找到他

修复此错误的提示

”类型系统。韦伯。MVC.RequredAttributeAdapter 必须有一个公共构造函数,它接受 System 类型的三个参数。韦伯。MVC.系统模型元数据。韦伯。MVC.ControllerContext 和 Expressive批注。属性。RequredIfAttribute 参数名称: AdapterType”

技巧 # 1: 确保您从“ ValidationAttribute 验证属性”继承而不是从“ RequredAttribute 必需属性”继承

 public class RequiredIfAttribute : ValidationAttribute, IClientValidatable { ...}

技巧 # 2: 或从‘ Global.asax’中删除整行代码,在新版本的代码中(在@Dan _ Hunex 编辑之后)根本不需要这一行代码,是的,在旧版本中这一行是必须的..。

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

获得 Javascript 代码部分工作的提示

1-将代码放入一个新的 js 文件中(例如: requdIfValidator.js)

2-翘曲 $(document) . ready (function (){ ... ... })中的代码;

3-在包含 JQuery 验证库之后包含我们的 js 文件,所以现在看起来是这样的:

@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>

编辑 C # 代码

来自

rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);

rule.ValidationParameters["dependentproperty"] = PropertyName;

if (dependentValue.ToString() == DesiredValue.ToString())

if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())

我的整个代码启动并运行

Global.asax

没什么要补充的,保持干净

验证程序

在 ~/content 或 ~/script 文件夹中创建此文件

    $.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options)
{
options.rules['requiredif'] = options.params;
options.messages['requiredif'] = options.message;
});




$(document).ready(function ()
{


$.validator.addMethod('requiredif', function (value, element, parameters) {
var desiredvalue = parameters.desiredvalue;
desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
var actualvalue = {}
if (controlType == "checkbox" || controlType == "radio") {
var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
actualvalue = control.val();
} else {
actualvalue = $("#" + parameters.dependentproperty).val();
}
if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
var isValid = $.validator.methods.required.call(this, value, element, parameters);
return isValid;
}
return true;
});
});

_ Layout.cshtml 或 View

@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>

Requiredifattribute.cs

在项目的某个位置创建它,例如在 ~/model/customValization/

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


namespace Your_Project_Name.Models.CustomValidation
{
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
private readonly RequiredAttribute _innerAttribute;


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


protected override ValidationResult IsValid(object value, ValidationContext context)
{
var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);


if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
}
}
return ValidationResult.Success;
}


public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredif",
};
rule.ValidationParameters["dependentproperty"] = PropertyName;
rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;


yield return rule;
}
}
}

模型

    using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using Your_Project_Name.Models.CustomValidation;


namespace Your_Project_Name.Models.ViewModels
{


public class CreateOpenActivity
{
public Nullable<int> ORG_BY_CD { get; set; }


[RequiredIf("ORG_BY_CD", "5", ErrorMessage = "Coordinator ID is required")] // This means: IF 'ORG_BY_CD' is equal 5 (for the example)  > make 'COR_CI_ID_NUM' required and apply its all validation / data annotations
[RegularExpression("[0-9]+", ErrorMessage = "Enter Numbers Only")]
[MaxLength(9, ErrorMessage = "Enter a valid ID Number")]
[MinLength(9, ErrorMessage = "Enter a valid ID Number")]
public string COR_CI_ID_NUM { get; set; }
}
}

观点

其实没什么好说的。

    @model Your_Project_Name.Models.ViewModels.CreateOpenActivity
@{
ViewBag.Title = "Testing";
}


@using (Html.BeginForm())
{
@Html.AntiForgeryToken()


<div class="form-horizontal">
<h4>CreateOpenActivity</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })


<div class="form-group">
@Html.LabelFor(model => model.ORG_BY_CD, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ORG_BY_CD, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ORG_BY_CD, "", new { @class = "text-danger" })
</div>
</div>


<div class="form-group">
@Html.LabelFor(model => model.COR_CI_ID_NUM, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.COR_CI_ID_NUM, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.COR_CI_ID_NUM, "", new { @class = "text-danger" })
</div>
</div>


<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}

我可能稍后会上传一个项目样本..。

希望这对你有帮助

谢谢你

在 Adel Mourad 和 Dan Hunex 的注释的基础上,我修改了代码,以提供一个只接受与给定值匹配的 不要值的示例。

我还发现我不需要 JavaScript。

我将以下类添加到我的 Model 文件夹中:

public class RequiredIfNotAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
private Object InvalidValue { get; set; }
private readonly RequiredAttribute _innerAttribute;


public RequiredIfNotAttribute(String propertyName, Object invalidValue)
{
PropertyName = propertyName;
InvalidValue = invalidValue;
_innerAttribute = new RequiredAttribute();
}


protected override ValidationResult IsValid(object value, ValidationContext context)
{
var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);


if (dependentValue.ToString() != InvalidValue.ToString())
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
}
}
return ValidationResult.Success;
}


public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredifnot",
};
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
rule.ValidationParameters["invalidvalue"] = InvalidValue is bool ? InvalidValue.ToString().ToLower() : InvalidValue;


yield return rule;
}

我不需要对视图进行任何更改,但是确实对模型的属性进行了更改:

    [RequiredIfNot("Id", 0, ErrorMessage = "Please select a Source")]
public string TemplateGTSource { get; set; }


public string TemplateGTMedium
{
get
{
return "Email";
}
}


[RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Campaign")]
public string TemplateGTCampaign { get; set; }


[RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Term")]
public string TemplateGTTerm { get; set; }

希望这个能帮上忙!

我认为使用 IValidatableObject是一个很好的选择。

public class MyInputModel : IValidateObject
{
[Required]
public int Id {get;set;}


public string MyProperty1 {get;set;}
public string MyProperty2 {get;set;}
public bool MyProperty3 {get;set;}


public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (MyProperty1==null&&MyProperty2==null&&MyPropterty3!=false) //whatever condition
{
yield return new ValidationResult(
"Custom complex error");
}
}


}