65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (11投票s)

2014年6月11日

CPOL

1分钟阅读

viewsIcon

74861

downloadIcon

2048

这是一个在 ASP.NET MVC 5 中添加自定义数据注解和客户端验证的技巧。

引言

这是一个在 ASP.NET MVC 5 中添加自定义比较数据注解和客户端验证的技巧。主要目标是在 ViewModel 的两个属性之间,或在同一表单的两个相似列之间,使用 <、>、<=、>= 运算符进行比较验证,适用于数字和日期时间数据类型。

edit

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 会显示错误。

无效条目

有效条目

© . All rights reserved.