MVC中如何实现两个字段大小比较的关联校验(例如起止日期校验)
解决办法总览
你可以根据是否需要客户端实时校验的需求,选择以下3种可行方案:
方案1:实现IValidatableObject接口(仅服务端校验,实现成本最低)
直接让Hospitalisation类继承IValidatableObject接口,实现Validate方法写入关联校验逻辑即可,服务端接收到模型绑定数据后会自动触发校验:
using System.ComponentModel.DataAnnotations; public class Hospitalisation : IValidatableObject { [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")] [DataType(DataType.Date)] [Display(Name = "Starting Date")] public string StartDate { get; set; } = string.Empty; [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")] [DataType(DataType.Date)] [Display(Name = "Ending Date")] public string EndDate { get; set; } = string.Empty; public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { // 先校验日期格式合法性 if (DateTime.TryParse(StartDate, out var startDate) && DateTime.TryParse(EndDate, out var endDate)) { if (startDate >= endDate) { // 第二个参数指定错误关联的字段,可同时给两个字段挂载错误提示 yield return new ValidationResult("开始日期必须早于结束日期", new[] { nameof(StartDate), nameof(EndDate) }); } } } }
使用时只需要在Controller对应的Action中判断ModelState.IsValid,即可自动获取校验结果和错误信息。
方案2:自定义校验特性(同时支持服务端+客户端侧校验)
如果需要前端不提交表单就实时触发校验,可自定义校验特性同时实现客户端校验适配:
- 先定义自定义校验特性和枚举
public class DateCompareAttribute : ValidationAttribute, IClientModelValidator { private readonly string _targetPropertyName; private readonly ComparisonType _comparisonRule; public DateCompareAttribute(string targetPropertyName, ComparisonType comparisonRule) { _targetPropertyName = targetPropertyName; _comparisonRule = comparisonRule; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // 获取要对比的字段值 var targetProperty = validationContext.ObjectType.GetProperty(_targetPropertyName); if (targetProperty == null) return new ValidationResult($"关联字段{_targetPropertyName}不存在"); var targetValue = targetProperty.GetValue(validationContext.ObjectInstance); if (value == null || targetValue == null) return ValidationResult.Success; if (DateTime.TryParse(value.ToString(), out var currentDate) && DateTime.TryParse(targetValue.ToString(), out var targetDate)) { var checkResult = _comparisonRule switch { ComparisonType.EarlierThan => currentDate < targetDate, ComparisonType.LaterThan => currentDate > targetDate, _ => true }; if (!checkResult) return new ValidationResult(ErrorMessage ?? "日期校验不通过"); } return ValidationResult.Success; } // 注入客户端校验需要的属性 public void AddValidation(ClientModelValidationContext context) { context.Attributes.TryAdd("data-val", "true"); context.Attributes.TryAdd("data-val-datecompare", ErrorMessage ?? "开始日期必须早于结束日期"); context.Attributes.TryAdd("data-val-datecompare-target", _targetPropertyName); } } public enum ComparisonType { EarlierThan, LaterThan }
- 给模型字段挂载特性
[DateCompare(nameof(EndDate), ComparisonType.EarlierThan, ErrorMessage = "开始日期必须早于结束日期")] [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")] [DataType(DataType.Date)] [Display(Name = "Starting Date")] public string StartDate { get; set; } = string.Empty;
- 补充前端自定义校验规则(项目已引入jQuery Validation的前提下)
// 注册自定义校验规则 $.validator.addMethod("datecompare", function (value, element, targetField) { const targetValue = $(`#${targetField}`).val(); // 任一字段为空时跳过校验,由必填特性负责判空 if (!value || !targetValue) return true; return new Date(value) < new Date(targetValue); }); // 适配unobtrusive校验 $.validator.unobtrusive.adapters.addSingleVal("datecompare", "target");
方案3:修复Remote特性传参问题
之前Remote特性无法获取关联字段值是因为没有指定额外传参,只要添加AdditionalFields配置即可:
// 配置额外传递EndDate字段值给校验接口 [Remote(action: "CheckDateRange", controller: "Home", AdditionalFields = nameof(EndDate), ErrorMessage = "开始日期必须早于结束日期")] public string StartDate { get; set; } = string.Empty;
对应Controller中的校验接口写法:
public IActionResult CheckDateRange(string StartDate, string EndDate) { if (DateTime.TryParse(StartDate, out var start) && DateTime.TryParse(EndDate, out var end)) { if (start >= end) return Json(false); } return Json(true); }
内容的提问来源于stack exchange,提问作者user16511868
相关产品推荐
相关产品推荐

