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

Hus*_*man 17 c# validation asp.net-core

有没有办法在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");
}
Run Code Online (Sandbox Code Playgroud)

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

    public class CompareDates : ValidationAttribute
{
    public CompareDates()
        : base("") { }

    public override bool IsValid(object value)
    {
        return base.IsValid(value);
    }

}
Run Code Online (Sandbox Code Playgroud)

我发现其他SO帖子建议使用另一个库,但我更喜欢坚持ValidationAttribute,如果这是可行的.

Ale*_*ore 35

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:如果您需要对此属性进行客户端验证,则需要实现一个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

AddValidation方法将为您的输入添加属性context.Attributes.

在此输入图像描述

你可以在这里阅读更多IClientModelValidator

  • 我在 ASP.NET Core 中创建了一个包含最常见自定义验证的库。该库具有针对所有服务器端自定义验证的客户端验证。该库还解决了 OP 的单一属性问题,如下所示: `[CompareTo(nameof(EndDate), ComparisionType.SmallerThan)] public DateTime StartDate { get; 放; }`。这是该库的链接:https://github.com/TanvirArjel/AspNetCore.CustomValidation (2认同)

Cur*_*dev 13

作为一种可能的选择自我验证:

您只需要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" });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


rsc*_*ach 6

基于 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在预订上下文中,示例如下:

public DateTime CheckInDate { get; set; }

[Comparison("CheckInDate", ComparisonType.EqualTo, ErrorMessage = "CheckOutDate must be equal to CheckInDate")]
public DateTime CheckOutDate { get; set; }
Run Code Online (Sandbox Code Playgroud)

  • 这对于非空值很有用,但如果您在对值调用 .GetType() 之前放置 if(null != value)... 和一个附加的 if(null != ComparisonValue) 语句以避免更好空引用异常。 (2认同)

Jai*_*rín 5

根据 Alexander Gore 的回复,我建议进行更好的通用验证(并且它与 .Net core 兼容)。当您想要使用 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

比...更棒

[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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在预订上下文中,示例如下:

public DateTime CheckInDate { get; set; }

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


Tan*_*jel 5

我创建了一个包含 ASP.NET Core 中最常见的自定义验证的库。该库还具有所有服务器端自定义验证的客户端验证。该库通过单个属性解决了 OP 的问题,如下所示:

\n\n
// If you want the StartDate to be smaller than the EndDate:\n[CompareTo(nameof(EndDate), ComparisionType.SmallerThan)] \npublic DateTime StartDate { get; set; }\n
Run Code Online (Sandbox Code Playgroud)\n\n

以下是该库的 GitHub 链接:AspNetCore.CustomValidation

\n\n

目前,该库包含以下验证属性:

\n\n

1. FileAttribute - 验证文件类型、文件最大大小、文件最小大小;

\n\n

2. MaxAgeAttribute - 根据 DateTime 类型的出生日期值验证最大年龄;

\n\n

3. MinAgeAttribute - 根据 DateTime 类型的出生日期值验证所需的最低年龄;

\n\n

4. MaxDateAttribute - 设置日期时间字段的最大值验证;

\n\n

5. MinDateAttribute - 设置日期时间字段的最小值验证;

\n\n

6. CompareToAttibute \xe2\x80\x93 将一个属性值与另一个属性值进行比较;

\n\n

7. TinyMceRequiredAttribute - 在在线文本编辑器(如 TinyMCE、CkEditor 等)上强制执行必需的验证属性。

\n