给定以下viewmodel:
public class Someviewmodel { public bool IsA { get; set; } public bool IsB { get; set; } public bool IsC { get; set; } //... other properties }
我希望创建一个自定义属性,验证至少一个可用属性为true。我想象能够附加一个属性到一个属性,并像这样分配一个组名:
public class Someviewmodel { [RequireAtLeastOneOfGroup("Group1")] public bool IsA { get; set; } [RequireAtLeastOneOfGroup("Group1")] public bool IsB { get; set; } [RequireAtLeastOneOfGroup("Group1")] public bool IsC { get; set; } //... other properties [RequireAtLeastOneOfGroup("Group2")] public bool IsY { get; set; } [RequireAtLeastOneOfGroup("Group2")] public bool IsZ { get; set; } }
我想在客户端验证在表单提交之前作为表单更改的值,这就是为什么我喜欢避免类级属性(如果可能的话)。
这需要服务器端验证和客户端验证,以定位所有具有相同组名称值的属性作为自定义属性的参数传入。这可能吗?任何指导是非常赞赏。
解决方法
这里有一种方法继续(还有其他方法,我只是说明一个将匹配您的视图模型是):
[AttributeUsage(AttributeTargets.Property)] public class RequireAtLeastOneOfGroupAttribute: ValidationAttribute,IClientValidatable { public RequireAtLeastOneOfGroupAttribute(string groupName) { ErrorMessage = string.Format("You must select at least one value from group \"{0}\"",groupName); GroupName = groupName; } public string GroupName { get; private set; } protected override ValidationResult IsValid(object value,ValidationContext validationContext) { foreach (var property in GetGroupProperties(validationContext.ObjectType)) { var propertyValue = (bool)property.GetValue(validationContext.ObjectInstance,null); if (propertyValue) { // at least one property is true in this group => the model is valid return null; } } return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); } private IEnumerable<PropertyInfo> GetGroupProperties(Type type) { return from property in type.GetProperties() where property.PropertyType == typeof(bool) let attributes = property.GetCustomAttributes(typeof(RequireAtLeastOneOfGroupAttribute),false).OfType<RequireAtLeastOneOfGroupAttribute>() where attributes.Count() > 0 from attribute in attributes where attribute.GroupName == GroupName select property; } public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata Metadata,ControllerContext context) { var groupProperties = GetGroupProperties(Metadata.ContainerType).Select(p => p.Name); var rule = new ModelClientValidationRule { ErrorMessage = this.ErrorMessage }; rule.ValidationType = string.Format("group",GroupName.ToLower()); rule.ValidationParameters["propertynames"] = string.Join(",",groupProperties); yield return rule; } }
现在,让我们定义一个控制器:
public class HomeController : Controller { public ActionResult Index() { var model = new Someviewmodel(); return View(model); } [HttpPost] public ActionResult Index(Someviewmodel model) { return View(model); } }
和一个视图:
@model Someviewmodel <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.EditorFor(x => x.IsA) @Html.ValidationMessageFor(x => x.IsA) <br/> @Html.EditorFor(x => x.IsB)<br/> @Html.EditorFor(x => x.IsC)<br/> @Html.EditorFor(x => x.IsY) @Html.ValidationMessageFor(x => x.IsY) <br/> @Html.EditorFor(x => x.IsZ)<br/> <input type="submit" value="OK" /> }
剩下的最后一部分是注册适配器以进行客户端验证:
jQuery.validator.unobtrusive.adapters.add( 'group',[ 'propertynames' ],function (options) { options.rules['group'] = options.params; options.messages['group'] = options.message; } ); jQuery.validator.addMethod('group',function (value,element,params) { var properties = params.propertynames.split(','); var isValid = false; for (var i = 0; i < properties.length; i++) { var property = properties[i]; if ($('#' + property).is(':checked')) { isValid = true; break; } } return isValid; },'');
根据您的具体要求,代码可能会进行调整。