RequiredIf条件validation属性
我正在寻找一些关于实现validation属性的最佳方式的build议,这个属性可以做到以下几点。
模型
public class MyInputModel { [Required] public int Id {get;set;} public string MyProperty1 {get;set;} public string MyProperty2 {get;set;} public bool MyProperty3 {get;set;} }
我想拥有一个价值至lessprop1 prop2 prop3,如果prop3是唯一的价值,它不应该等于false。 我将如何去写一个validation属性(s?)呢?
感谢您的帮助!
您可以检出以下博客文章,了解[RequiredIf]
自定义validation属性的示例实现。 它与单个其他属性值进行比较,但您可以轻松调整IsValid
方法以满足您的要求。
我昨天也遇到了同样的问题,但是我以非常干净的方式完成了客户端和服务器端validation。
条件 :根据模型中其他属性的值,您需要创build另一个属性。 这是代码
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(属性)的特定值,您的其他属性必须根据需要进行validation
假设你有以下几点:
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; } }
最后但并非最不重要的,为你的属性注册适配器,以便它可以做客户端validation(我把它放在global.asax,Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredAttributeAdapter));
EDITED
有人抱怨说客户端无论发生什么事情都不行, 所以我修改了上面的代码,用JavaScript做了有条件的客户端validation。 对于这种情况你不需要注册适配器
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(捆绑它和renderit …放在它自己的脚本文件中)
$.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; });
您显然需要将不被忽略的validationjquery作为要求包含在内
我知道这个话题是前些时候问过的,但是最近我遇到了类似的问题,又find了另外一个,但是在我看来是一个更完整的解决scheme。 我决定实现提供条件属性的机制,根据逻辑expression式中定义的其他属性值和它们之间的关系来计算validation结果。
使用它你可以通过以下方式获得你所问的结果:
[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; }
有关ExpressiveAnnotations库的更多信息可以在这里find 。 它应该简化许多声明性validation案例,而无需编写额外的特定于案例的属性或在控制器内部使用命令式validation方式。
我得到它在ASP.NET MVC 5上工作
我看到很多人对这个代码感兴趣并且受到这个代码的困扰,我知道这是第一次令人困惑和混乱。
笔记
- 在服务器端和客户端使用MVC 5:D
- 我没有安装“ExpressiveAnnotations”库。
- 我正在采取“ @Dan Hunex ”的原始代码,在上面find他
提示来解决这个错误
“typesSystem.Web.Mvc.RequiredAttributeAdapter必须有一个公共构造函数,它接受typesSystem.Web.Mvc.ModelMetadata,System.Web.Mvc.ControllerContext和ExpressiveAnnotations.Attributes.RequiredIfAttribute的三个参数参数名称:adapterType”
提示#1:确保您从“ ValidationAttribute ”而不是从“ RequiredAttribute ”inheritance
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable { ...}
提示#2:或从“ Global.asax ”中删除这整行,在新版本的代码中(在@Dan_Hunex编辑之后)完全不需要,而且这行在旧版本中是必须的。 。
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredAttributeAdapter));
提示获取Javascript代码部分工作
1-将代码放入一个新的js文件(例如:requiredIfValidator.js)
2,将$(document).ready(function(){……..})中的代码扭曲;
在包含JQueryvalidation库之后,请包括我们的js文件,现在看起来像这样:
@Scripts.Render("~/bundles/jqueryval") <script src="~/Content/JS/requiredIfValidator.js"></script>
4-编辑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中
没有什么要加在这里,保持干净
requiredIfValidator.js
在〜/内容或〜/ scripts文件夹中创build这个文件
$.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或视图
@Scripts.Render("~/bundles/jqueryval") <script src="~/Content/JS/requiredIfValidator.js"></script>
RequiredIfAttribute.cs类
创build它在你的项目中的一些地方,例如在〜/ models / customValidation /
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> }
我可以稍后上传一个项目样本…
希望这是有帮助的
谢谢
如果您尝试使用“ModelState.Remove”或“ModelState [”Prop“] .Errors.Clear()”,“ModelState.IsValid”stil返回false。
为什么不从模型中删除默认的“必需”注释,并在Controller的“Post”操作上的“ModelState.IsValid”之前进行自定义validation? 喜欢这个:
if (!String.IsNullOrEmpty(yourClass.Property1) && String.IsNullOrEmpty(yourClass.dependantProperty)) ModelState.AddModelError("dependantProperty", "It´s necessary to select some 'dependant'.");
与其他解决scheme的主要区别在于,这个在服务器端的RequiredAttribute
重用逻辑,并且在客户端使用required
的validation方法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; });
我扩展了Adel Mourad和Dan Hunex的笔记,修改了代码以提供一个只接受与给定值不匹配的值的示例。
我也发现我不需要JavaScript。
我将下面的类添加到我的Models文件夹中:
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; }
希望这可以帮助!
在视图模型中,对于您希望根据另一个字段的值validation的每个字段,执行以下操作:
public string FieldName { get; set; } [RequiredIf("FieldName", ConditionTypes.Equals, condition)] public string FieldName1 { get; set; }