自定义validation属性:比较同一模型中的两个属性

有没有办法在ASP.NET Core中创建自定义属性,以使用ValidationAttributevalidation一个日期属性是否小于模型中的其他日期属性。

让我说我有这个:

 public class MyViewModel { [Required] [CompareDates] public DateTime StartDate { get; set; } [Required] public DateTime EndDate { get; set; } = DateTime.Parse("3000-01-01"); } 

我想尝试使用这样的东西:

  public class CompareDates : ValidationAttribute { public CompareDates() : base("") { } public override bool IsValid(object value) { return base.IsValid(value); } } 

我发现其他SOpost建议使用另一个库,但如果可行,我更喜欢坚持使用ValidationAttribute。

您可以为比较两个属性创建自定义validation属性。 这是服务器端validation:

 public class MyViewModel { [DateLessThan("End", ErrorMessage = "Not valid")] public DateTime Begin { get; set; } public DateTime End { get; set; } } public class DateLessThanAttribute : ValidationAttribute { private readonly string _comparisonProperty; public DateLessThanAttribute(string comparisonProperty) { _comparisonProperty = comparisonProperty; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { ErrorMessage = ErrorMessageString; var currentValue = (DateTime)value; var property = validationContext.ObjectType.GetProperty(_comparisonProperty); if (property == null) throw new ArgumentException("Property with this name not found"); var comparisonValue = (DateTime)property.GetValue(validationContext.ObjectInstance); if (currentValue > comparisonValue) return new ValidationResult(ErrorMessage); return ValidationResult.Success; } } 

更新 :如果您需要对此属性进行客户端validation,则需要实现IClientModelValidator接口:

 public class DateLessThanAttribute : ValidationAttribute, IClientModelValidator { ... public void AddValidation(ClientModelValidationContext context) { var error = FormatErrorMessage(context.ModelMetadata.GetDisplayName()); context.Attributes.Add("data-val", "true"); context.Attributes.Add("data-val-error", error); } } 

AddValidation方法将从context.Attributes向您的输入添加属性。

在此处输入图像描述

您可以在这里阅读更多IClientModelValidator

作为一种可能的选择自我validation

您只需要使用方法Validate实现接口IValidatableObject ,您可以在其中放置validation代码。

 public class MyViewModel : IValidatableObject { [Required] public DateTime StartDate { get; set; } [Required] public DateTime EndDate { get; set; } = DateTime.Parse("3000-01-01"); public IEnumerable Validate(ValidationContext validationContext) { int result = DateTime.Compare(StartDate , EndDate); if (result < 0) { yield return new ValidationResult("start date must be less than the end date!", new [] { "ConfirmEmail" }); } } } 

您可以在IsValid方法中比较两个日期。

 public class CompareDates : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { //get your startdate & end date from model and value //perform comparison if (StartDate < EndDate) { return new ValidationResult ("start date must be less than the end date"); } else { return ValidationResult.Success; } } } 

基于Alexander Gore的响应,我建议进行更好的通用validation(并且它与.Net核心兼容)。 如果要使用GreatherThan或LessThan逻辑(无论类型是什么)比较属性,您可以validation它们是否已实现IComparable接口。 如果两个属性都有效,则可以使用CompareTo实现。 此规则也适用于DateTime和数字类型

少于

 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] public class LessThanAttribute : ValidationAttribute { private readonly string _comparisonProperty; public LessThanAttribute(string comparisonProperty) { _comparisonProperty = comparisonProperty; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { ErrorMessage = ErrorMessageString; if (value.GetType() == typeof(IComparable)) { throw new ArgumentException("value has not implemented IComparable interface"); } var currentValue = (IComparable)value; var property = validationContext.ObjectType.GetProperty(_comparisonProperty); if (property == null) { throw new ArgumentException("Comparison property with this name not found"); } var comparisonValue = property.GetValue(validationContext.ObjectInstance); if (comparisonValue.GetType() == typeof(IComparable)) { throw new ArgumentException("Comparison property has not implemented IComparable interface"); } if (!ReferenceEquals(value.GetType(), comparisonValue.GetType())) { throw new ArgumentException("The properties types must be the same"); } if (currentValue.CompareTo((IComparable)comparisonValue) >= 0) { return new ValidationResult(ErrorMessage); } return ValidationResult.Success; } } 

比…更棒

 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] public class GreaterThanAttribute : ValidationAttribute { private readonly string _comparisonProperty; public GreaterThanAttribute(string comparisonProperty) { _comparisonProperty = comparisonProperty; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { ErrorMessage = ErrorMessageString; if (value.GetType() == typeof(IComparable)) { throw new ArgumentException("value has not implemented IComparable interface"); } var currentValue = (IComparable)value; var property = validationContext.ObjectType.GetProperty(_comparisonProperty); if (property == null) { throw new ArgumentException("Comparison property with this name not found"); } var comparisonValue = property.GetValue(validationContext.ObjectInstance); if (comparisonValue.GetType() == typeof(IComparable)) { throw new ArgumentException("Comparison property has not implemented IComparable interface"); } if (!ReferenceEquals(value.GetType(), comparisonValue.GetType())) { throw new ArgumentException("The properties types must be the same"); } if (currentValue.CompareTo((IComparable)comparisonValue) < 0) { return new ValidationResult(ErrorMessage); } return ValidationResult.Success; } } 

在预订环境中,示例可以如下:

 public DateTime CheckInDate { get; set; } [GreaterThan("CheckInDate", ErrorMessage = "CheckOutDate must be greater than CheckInDate")] public DateTime CheckOutDate { get; set; }