ASP.NET MVC 自定义比较数据注解与客户端验证






4.67/5 (11投票s)
这是一个在 ASP.NET MVC 5 中添加自定义数据注解和客户端验证的技巧。
引言
这是一个在 ASP.NET MVC 5 中添加自定义比较数据注解和客户端验证的技巧。主要目标是在 ViewModel 的两个属性之间,或在同一表单的两个相似列之间,使用 <、>、<=、>= 运算符进行比较验证,适用于数字和日期时间数据类型。
Using the Code
我正在创建一个小的 ASP.NET MVC 5 应用程序,并创建一个自定义属性类,通过继承 System.ComponentModel.DataAnnotations.ValidationAttribute
来放置基本验证逻辑,并通过 System.Web.Mvc.IClientValidatable
来将验证属性渲染到元素上。
测试环境
- Visual Studio 2013
- ASP.NET MVC 5
- jquery-1.10.2.js
- jquery.validate.js
- 整数、实数、日期和时间数据类型
将比较运算符定义为 enum
。
genericcompare.cs 在 .NET C# 5 中
public enum GenericCompareOperator
{
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual
}
定义用于比较属性的属性类
public sealed class GenericCompareAttribute : ValidationAttribute, IClientValidatable
{
private GenericCompareOperator operatorname = GenericCompareOperator.GreaterThanOrEqual;
public string CompareToPropertyName { get; set; }
public GenericCompareOperator OperatorName { get { return operatorname; } set { operatorname = value; } }
// public IComparable CompareDataType { get; set; }
public GenericCompareAttribute() : base() { }
//Override IsValid
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string operstring = (OperatorName == GenericCompareOperator.GreaterThan ?
"greater than " : (OperatorName == GenericCompareOperator.GreaterThanOrEqual ?
"greater than or equal to " :
(OperatorName == GenericCompareOperator.LessThan ? "less than " :
(OperatorName == GenericCompareOperator.LessThanOrEqual ? "less than or equal to " : ""))));
var basePropertyInfo = validationContext.ObjectType.GetProperty(CompareToPropertyName);
var valOther = (IComparable)basePropertyInfo.GetValue(validationContext.ObjectInstance, null);
var valThis = (IComparable)value;
if ((operatorname == GenericCompareOperator.GreaterThan && valThis.CompareTo(valOther) <= 0) ||
(operatorname == GenericCompareOperator.GreaterThanOrEqual && valThis.CompareTo(valOther) < 0) ||
(operatorname == GenericCompareOperator.LessThan && valThis.CompareTo(valOther) >= 0) ||
(operatorname == GenericCompareOperator.LessThanOrEqual && valThis.CompareTo(valOther) > 0))
return new ValidationResult(base.ErrorMessage);
return null;
}
#region IClientValidatable Members
public IEnumerable<ModelClientValidationRule>
GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
ModelClientValidationRule compareRule = new ModelClientValidationRule();
compareRule.ErrorMessage = errorMessage;
compareRule.ValidationType = "genericcompare";
compareRule.ValidationParameters.Add("comparetopropertyname", CompareToPropertyName);
compareRule.ValidationParameters.Add("operatorname", OperatorName.ToString());
yield return compareRule;
}
#endregion
}
customannotation.js:
$.validator.addMethod("genericcompare", function (value, element, params) {
// debugger;
var propelename = params.split(",")[0];
var operName = params.split(",")[1];
if (params == undefined || params == null || params.length == 0 ||
value == undefined || value == null || value.length == 0 ||
propelename == undefined || propelename == null || propelename.length == 0 ||
operName == undefined || operName == null || operName.length == 0)
return true;
var valueOther = $(propelename).val();
var val1 = (isNaN(value) ? Date.parse(value) : eval(value));
var val2 = (isNaN(valueOther) ? Date.parse(valueOther) : eval(valueOther));
if (operName == "GreaterThan")
return val1 > val2;
if (operName == "LessThan")
return val1 < val2;
if (operName == "GreaterThanOrEqual")
return val1 >= val2;
if (operName == "LessThanOrEqual")
return val1 <= val2;
})
;$.validator.unobtrusive.adapters.add("genericcompare",
["comparetopropertyname", "operatorname"], function (options) {
options.rules["genericcompare"] = "#" +
options.params.comparetopropertyname + "," + options.params.operatorname;
options.messages["genericcompare"] = options.message;
});
以下是 viewmodel
类,用于将注解应用于比较 EndDate
与 StartDate
属性,以及比较 NumTo
与 NumFrom
。错误消息可以包含在资源中并通过属性引用,也可以使用 ErrorMessage
属性在属性中指定 errormessage
。
public class mymodel
{
[Display(Name = "Start Date:")]
[DataType(DataType.Date)]
public DateTime? StartDate { get; set; }
[Display(Name = "End Date:")]
[DataType(DataType.Date)]
[GenericCompare(CompareToPropertyName= "StartDate",OperatorName= GenericCompareOperator.GreaterThanOrEqual,ErrorMessageResourceName="resourcekey",
ErrorMessageResourceType=typeof(resourceclassname))]
public DateTime? EndDate { set; get; }
[Display(Name = "Number From:")]
public int? NumFrom { get; set; }
[Display(Name = "Number To:")]
[GenericCompare(CompareToPropertyName = "NumFrom",
OperatorName = GenericCompareOperator.GreaterThanOrEqual,
ErrorMessageResourceName = "resourcekey",
ErrorMessageResourceType = typeof(resourceclassname))]
public int? NumTo { set; get; }
}
编写测试控制器类:该控制器视图中包含 index.cshtml
public class TestCompareController : Controller
{
//
// GET: /TestCompare/
public ActionResult Index()
{
return View();
}
}
这是为 MyModel
设计的视图
Index.cshtml
@model customcompare_MVC.Models.MyModel
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken() <div class="form-horizontal">
<h4>MyModel</h4>
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(model => model.StartDate, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.StartDate)
@Html.ValidationMessageFor(model => model.StartDate)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.EndDate, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.EndDate)
@Html.ValidationMessageFor(model => model.EndDate)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.NumFrom, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.NumFrom)
@Html.ValidationMessageFor(model => model.NumFrom)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.NumTo, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.NumTo)
@Html.ValidationMessageFor(model => model.NumTo)
</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>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@*script section defined in views/shared/_layout.cshtml*@
@section Scripts {
@*This bundle created in App_Start/bundleconfig.cs and registered the bundle in application_start event in global.asax.cs*@
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/Scripts/customcompare.js")
}
运行应用程序,如果结束日期小于开始日期,则结束日期会显示错误,如果数字 To 大于或等于数字 From,则数字 To 会显示错误。
无效条目
有效条目