自定义验证属性在同一字段上多次

Sat*_*ish 5 c# jquery-validate data-annotations asp.net-mvc-3

如何在同一字段上多次使用相同的自定义验证属性,或者只是为服务器端和客户端验证启用AllowMultiple = true?

我有以下自定义验证属性:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, 
        AllowMultiple = true, Inherited = true)]
public class RequiredIfAttribute : ValidationAttribute,IClientValidatable
{
    public RequiredIfAttribute(string dependentProperties, 
     string dependentValues = "",
     string requiredValue = "val")
     {
     }
}
Run Code Online (Sandbox Code Playgroud)

在dependentProperties中,我可以指定由逗号分隔的多个依赖属性,在dependentValues中,我可以指定依赖属性验证应该处理哪些值,最后在requiredValue中,我可以指定要验证的字段的期望值.

在我的模型中有两个属性LandMark,PinCode和我想使用验证如下:

public string LandMark { get; set; }
[RequiredIf("LandMark","XYZ","500500")]
[RequiredIf("LandMark", "ABC", "500505")]
public string PinCode { get; set; }
Run Code Online (Sandbox Code Playgroud)

这里的值只是例如,因为似乎我可以多次添加属性并且不会出现任何编译错误,我已经在属性中实现了TypeID,如果我从中删除客户端验证,它在服务器端运行良好.但是当我在属性上实现IClientValidatable时,它给了我一个错误:

"不引人注目的客户端验证规则中的验证类型名称必须是唯一的."

任何帮助我该如何解决?

Kyl*_*Mit 6

问题

验证属性有两个可以验证的环境:

  1. 服务器
  2. 客户

服务器验证 - 多属性简单

如果您有任何属性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class RequiredIfAttribute : ValidationAttribute
Run Code Online (Sandbox Code Playgroud)

并将其放在您的类属性上,如下所示:

public class Client
{
    public short ResidesWithCd { get; set; };

    [RequiredIf(nameof(ResidesWithCd), new[] { 99 }, "Resides with other is required.")]
    public string ResidesWithOther { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后任何时候服务器验证一个对象(例如ModelState.IsValid,它将检查ValidationAttribute每个属性并调用.IsValid()以确定有效性。即使 设置为 true ,这也能正常工作。AttributeUsage.AllowMultiple

客户端验证 - HTML 属性瓶颈

如果您通过实现IClientValidatable这样的方式启用客户端:

public class Client
{
    public short ResidesWithCd { get; set; };

    [RequiredIf(nameof(ResidesWithCd), new[] { 99 }, "Resides with other is required.")]
    public string ResidesWithOther { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后 ASP.NET 在生成时会发出以下 HTML:(
只要启用了ClientValidationEnabled& UnobtrusiveJavaScriptEnabled

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)    
{
    var modelClientValidationRule = new ModelClientValidationRule
    {
        ValidationType = "requiredif",
        ErrorMessage = ErrorMessageString
    };
    modelClientValidationRule.ValidationParameters.Add("target", prop.PropName);
    modelClientValidationRule.ValidationParameters.Add("values", prop.CompValues);

    return new List<ModelClientValidationRule> { modelClientValidationRule };
}
Run Code Online (Sandbox Code Playgroud)

数据属性是我们将规则转储到客户端验证引擎的唯一工具,客户端验证引擎将通过内置或自定义适配器搜索页面上的任何属性。一旦成为客户端规则集的一部分,它将能够使用内置或自定义方法确定每个解析规则的有效性。

因此,我们可以通过添加自定义适配器来使用 jQuery Validate Unobtrusive 来查找和解析这些属性,该适配器将向引擎添加验证规则:

// hook up to client side validation
$.validator.unobtrusive.adapters.add('requiredif', ['target', 'values'], function (options) {
    options.rules["requiredif"] = {
        id: '#' + options.params.target,
        values: JSON.parse(options.params.values)
    };
    options.messages['requiredif'] = options.message;
});
Run Code Online (Sandbox Code Playgroud)

然后,我们可以通过添加这样的自定义方法来告诉该规则如何运行并确定有效性,该方法将添加一种自定义方法来评估requiredif规则(与日期规则或正则表达式规则相反),这些方法将依赖于我们之前通过适配器加载的参数:

// test validity
$.validator.addMethod('requiredif', function (value, element, params) {
    var targetHasCondValue = targetElHasValue(params.id, params.value);
    var requiredAndNoValue = targetHasCondValue && !value; // true -> :(
    var passesValidation = !requiredAndNoValue;            // true -> :)
    return passesValidation;
}, '');
Run Code Online (Sandbox Code Playgroud)

这一切都像这样:

客户端可验证

解决方案

所以我们学了什么?好吧,如果我们希望同一个规则在同一个元素上多次出现,适配器必须多次查看每个元素的确切规则集,无法区分多个集合中的每个实例。此外,ASP.NET 不会多次呈现相同的属性名称,因为它不是有效的 html。

所以,我们要么需要:

  1. 将所有客户端规则折叠成一个包含所有信息的超级属性
  2. 用每个实例编号重命名属性,然后找到一种方法来解析它们。

我将探索选项一(发出单个客户端属性),您可以通过以下几种方式:

  1. 创建一个接受多个元素的单个属性以在服务器客户端上进行验证
  2. 保留多个不同的服务器端属性,然后在发送到客户端之前通过反射合并所有属性

在任何一种情况下,您都必须重新编写客户端逻辑(适配器/方法)以获取一组值,而不是一次一个值。

我们将构建/传输一个 JSON 序列化对象,如下所示:

<input class="form-control" type="text" value=""
       id="Client_CommunicationModificationDescription" 
       name="Client.CommunicationModificationDescription" 
       data-val="true"
       data-val-requiredif="Communication Modification Description is required."
       data-val-requiredif-target="CommunicationModificationCd"
       data-val-requiredif-values="99" >
Run Code Online (Sandbox Code Playgroud)

Scripts/ValidateRequiredIfAny.js

以下是我们将如何在客户端适配器/方法中处理它:

// hook up to client side validation
$.validator.unobtrusive.adapters.add('requiredif', ['target', 'values'], function (options) {
    options.rules["requiredif"] = {
        id: '#' + options.params.target,
        values: JSON.parse(options.params.values)
    };
    options.messages['requiredif'] = options.message;
});
Run Code Online (Sandbox Code Playgroud)

Models/RequiredIfAttribute.cs

在服务器端,我们将像平常一样验证属性,但是当我们构建客户端属性时,我们将查找所有属性并构建一个超级属性

// test validity
$.validator.addMethod('requiredif', function (value, element, params) {
    var targetHasCondValue = targetElHasValue(params.id, params.value);
    var requiredAndNoValue = targetHasCondValue && !value; // true -> :(
    var passesValidation = !requiredAndNoValue;            // true -> :)
    return passesValidation;
}, '');
Run Code Online (Sandbox Code Playgroud)

然后我们可以通过同时应用多个属性将其绑定到我们的模型:

var props = [
  {
    PropName: "RoleCd",
    CompValues: ["2","3","4","5"]
  },
  {
    PropName: "IsPatient",
    CompValues: ["true"]
  }
]
Run Code Online (Sandbox Code Playgroud)

进一步阅读


Sat*_*ish 5

终于在这里,我找到了自己的答案.请查看以下文章以获得解决方案 http://www.codeproject.com/KB/validation/MultipleDataAnnotations.aspx