有没有办法在ASP.NET Core中创建自定义属性,以使用 ValidationAttribute
验证一个日期属性是否小于模型中的其他日期属性。
让我说我有这个:
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);
}
}
我发现其他SO帖子建议使用另一个库,但如果可行,我更愿意坚持使用ValidationAttribute。
答案 0 :(得分:30)
您可以为比较两个属性创建自定义验证属性。它是服务器端验证:
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;
}
}
<强>更新强>:
如果您需要对此属性进行客户端验证,则需要实现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
答案 1 :(得分:9)
作为一种可能的选择自我验证:
您只需要使用方法IValidatableObject
实现接口Validate
,您可以在其中放置验证码。
public class MyViewModel : IValidatableObject
{
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime EndDate { get; set; } = DateTime.Parse("3000-01-01");
public IEnumerable<ValidationResult> 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" });
}
}
}
答案 2 :(得分:3)
基于Alexander Gore的响应,我建议进行更好的通用验证(它与.Net核心兼容)。当您想使用GreatherThan或LessThan逻辑(无论类型如何)比较属性时,可以验证它们是否实现了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; }
答案 3 :(得分:2)
您可以比较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;
}
}
}
答案 4 :(得分:0)
基于Jaime的回答和Jeffrey的评论,即需要小于,小于或等于,等于,大于,大于或等于的单个属性。
下面的代码将使用单个属性处理所有条件。
public enum ComparisonType
{
LessThan,
LessThanOrEqualTo,
EqualTo,
GreaterThan,
GreaterThanOrEqualTo
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class ComparisonAttribute : ValidationAttribute
{
private readonly string _comparisonProperty;
private readonly ComparisonType _comparisonType;
public ComparisonAttribute(string comparisonProperty, ComparisonType comparisonType)
{
_comparisonProperty = comparisonProperty;
_comparisonType = comparisonType;
}
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");
}
bool compareToResult;
switch (_comparisonType)
{
case ComparisonType.LessThan:
compareToResult = currentValue.CompareTo((IComparable) comparisonValue) >= 0;
break;
case ComparisonType.LessThanOrEqualTo:
compareToResult = currentValue.CompareTo((IComparable) comparisonValue) > 0;
break;
case ComparisonType.EqualTo:
compareToResult = currentValue.CompareTo((IComparable) comparisonValue) != 0;
break;
case ComparisonType.GreaterThan:
compareToResult = currentValue.CompareTo((IComparable) comparisonValue) <= 0;
break;
case ComparisonType.GreaterThanOrEqualTo:
compareToResult = currentValue.CompareTo((IComparable) comparisonValue) < 0;
break;
default:
throw new ArgumentOutOfRangeException();
}
return compareToResult ? new ValidationResult(ErrorMessage) : ValidationResult.Success;
}
}
在预订环境中,示例如下:
public DateTime CheckInDate { get; set; }
[Comparison("CheckInDate", ComparisonType.EqualTo, ErrorMessage = "CheckOutDate must be equal to CheckInDate")]
public DateTime CheckOutDate { get; set; }
答案 5 :(得分:0)
这是我的看法。我的版本忽略为空的属性(可选)。当应用于Web API时,它非常合适。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class ComparisonAttribute : ValidationAttribute
{
private readonly string _comparisonProperty;
private readonly ComparisonType _comparisonType;
public ComparisonAttribute(string comparisonProperty, ComparisonType comparisonType)
{
_comparisonProperty = comparisonProperty;
_comparisonType = comparisonType;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ErrorMessage = ErrorMessageString;
var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
if (property == null)
throw new ArgumentException($"Property {_comparisonProperty} not found");
var right = property.GetValue(validationContext.ObjectInstance);
if (value is null || right is null)
return ValidationResult.Success;
if (value.GetType() == typeof(IComparable))
throw new ArgumentException($"The property {validationContext.MemberName} does not implement {typeof(IComparable).Name} interface");
if (right.GetType() == typeof(IComparable))
throw new ArgumentException($"The property {_comparisonProperty} does not implement {typeof(IComparable).Name} interface");
if (!ReferenceEquals(value.GetType(), right.GetType()))
throw new ArgumentException("The property types must be the same");
var left = (IComparable)value;
bool isValid;
switch (_comparisonType)
{
case ComparisonType.LessThan:
isValid = left.CompareTo((IComparable)right) < 0;
break;
case ComparisonType.LessThanOrEqualTo:
isValid = left.CompareTo((IComparable)right) <= 0;
break;
case ComparisonType.EqualTo:
isValid = left.CompareTo((IComparable)right) != 0;
break;
case ComparisonType.GreaterThan:
isValid = left.CompareTo((IComparable)right) > 0;
break;
case ComparisonType.GreaterThanOrEqualTo:
isValid = left.CompareTo((IComparable)right) >= 0;
break;
default:
throw new ArgumentOutOfRangeException();
}
return isValid
? ValidationResult.Success
: new ValidationResult(ErrorMessage);
}
public enum ComparisonType
{
LessThan,
LessThanOrEqualTo,
EqualTo,
GreaterThan,
GreaterThanOrEqualTo
}
}
答案 6 :(得分:0)
我在ASP.NET Core中创建了一个包含最常见的自定义验证的库。 该库还具有针对所有服务器端自定义验证的客户端验证。该库通过以下单个属性解决了OP的问题:
[CompareTo(nameof(EndDate), ComparisionType.SmallerThan)] // If you want StartDate should be smaller than Endate.
public DateTime StartDate { get; set; }
这是该库的github链接:AspNetCore.CustomValidation
当前,该库包含以下验证属性:
1。 FileAttribute -验证文件类型,文件最大大小,文件最小大小
2。 MaxAgeAttribute -根据DateTime类型的生日值验证最大年龄
3。 MinAgeAttribute -用于根据DateTime类型的生日值验证最低年龄要求。
4。 MaxDateAttribute -为DateTime字段设置最大值验证。
5。 MinDateAttribute -为DateTime字段设置最小值验证。
6。 CompareToAttibute –将一个属性值与另一个属性值进行比较。
7。 TinyMceRequiredAttribute -在TinyMCE,CkEditor等在线文本编辑器上强制执行必需的验证属性。