c#如何添加基于布尔值的自定义验证

c# How to Add Custom Validation Based on Boolean

我有一个Class赞

Public Class Employee
{
       [Required]
        public string FName { get; set; }
        [Required]
        public bool Married { get; set; }

        public string WifeName { get; set; }
        public string Wife_dOB { get; set; }

}

此处 WifeName And Wife_Dob 仅当 Married 为 True 时才需要 请帮助我如何解决这个问题 我在这里使用 MVC

您可以使用 custom validation attribute.

你可以看到我之前用过的一个解决方案:

/// <summary>
/// Provides conditional validation based on related property value.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class RequiredIfAttribute : ValidationAttribute
{
#region Properties

/// <summary>
/// Gets or sets the other property name that will be used during validation.
/// </summary>
/// <value>
/// The other property name.
/// </value>
public string OtherProperty { get; private set; }

/// <summary>
/// Gets or sets the display name of the other property.
/// </summary>
/// <value>
/// The display name of the other property.
/// </value>
public string OtherPropertyDisplayName { get; set; }

/// <summary>
/// Gets or sets the other property value that will be relevant for validation.
/// </summary>
/// <value>
/// The other property value.
/// </value>
public object OtherPropertyValue { get; private set; }

/// <summary>
/// Gets or sets a value indicating whether other property's value should match or differ from provided other property's value (default is <c>false</c>).
/// </summary>
/// <value>
///   <c>true</c> if other property's value validation should be inverted; otherwise, <c>false</c>.
/// </value>
/// <remarks>
/// How this works
/// - true: validated property is required when other property doesn't equal provided value
/// - false: validated property is required when other property matches provided value
/// </remarks>
public bool IsInverted { get; set; }

/// <summary>
/// Gets a value that indicates whether the attribute requires validation context.
/// </summary>
/// <returns><c>true</c> if the attribute requires validation context; otherwise, <c>false</c>.</returns>
public override bool RequiresValidationContext
{
    get { return true; }
}

#endregion

#region Constructor

/// <summary>
/// Initializes a new instance of the <see cref="RequiredIfAttribute"/> class.
/// </summary>
/// <param name="otherProperty">The other property.</param>
/// <param name="otherPropertyValue">The other property value.</param>
public RequiredIfAttribute(string otherProperty, object otherPropertyValue)
    : base("'{0}' is required because '{1}' has a value {3}'{2}'.")
{
    this.OtherProperty = otherProperty;
    this.OtherPropertyValue = otherPropertyValue;
    this.IsInverted = false;
}

#endregion

/// <summary>
/// Applies formatting to an error message, based on the data field where the error occurred.
/// </summary>
/// <param name="name">The name to include in the formatted message.</param>
/// <returns>
/// An instance of the formatted error message.
/// </returns>
public override string FormatErrorMessage(string name)
{
    return string.Format(
        CultureInfo.CurrentCulture,
        base.ErrorMessageString,
        name,
        this.OtherPropertyDisplayName ?? this.OtherProperty,
        this.OtherPropertyValue,
        this.IsInverted ? "other than " : "of ");
}

/// <summary>
/// Validates the specified value with respect to the current validation attribute.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="validationContext">The context information about the validation operation.</param>
/// <returns>
/// An instance of the <see cref="T:System.ComponentModel.DataAnnotations.ValidationResult" /> class.
/// </returns>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    if (validationContext == null)
    {
        throw new ArgumentNullException("validationContext");
    }

    PropertyInfo otherProperty = validationContext.ObjectType.GetProperty(this.OtherProperty);
    if (otherProperty == null)
    {
        return new ValidationResult(
            string.Format(CultureInfo.CurrentCulture, "Could not find a property named '{0}'.", this.OtherProperty));
    }

    object otherValue = otherProperty.GetValue(validationContext.ObjectInstance);

    // check if this value is actually required and validate it
    if (!this.IsInverted && object.Equals(otherValue, this.OtherPropertyValue) ||
        this.IsInverted && !object.Equals(otherValue, this.OtherPropertyValue))
    {
        if (value == null)
        {
            return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
        }

        // additional check for strings so they're not empty
        string val = value as string;
        if (val != null && val.Trim().Length == 0)
        {
            return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
        }
    }

    return ValidationResult.Success;
}

FluentValidation 可能是解决您的问题的好选择。使用 FluentValidation 可以很容易地创建一个 EmployeeValidator class 来根据需要执行尽可能多或尽可能少的验证。

注意:下面的例子是使用标准的 FluentValidation 包编写的,因为我不使用 MVC。除了标准的 nuget 包之外,似乎还有适用于多个版本的 MVC 的 nuget 包,使用这些实现可能会有所不同。


这里是一个如何使用 FluentValidation 的非常简单的例子:

void Main()
{
    var emp = new Employee { FName = "John Smith", Married = true };
    var val = new EmployeeValidator();
    val.ValidateAndThrow(emp);
}

public class Employee
{
    public string FName { get; set; }
    public bool Married { get; set; }
    public string WifeName { get; set; }
    public string Wife_dOB { get; set; }
}

public class EmployeeValidator : AbstractValidator<Employee>
{
    public EmployeeValidator()
    {
        RuleFor(e => e.WifeName).NotEmpty().When(e => e.Married);
        RuleFor(e => e.Wife_dOB).NotEmpty().When(e => e.Married);
    }
}

编辑 我忘了包含上面验证代码的输出示例。

示例输出:

ValidationException: Validation failed: -- WifeName: 'Wife Name' must not be empty. -- Wife_dOB: 'Wife_d OB' must not be empty.


另一种方法,在我看来是更好的方法,就像这样不需要验证,至少对于已婚 属性:

public class Person
{
    public Person(string firstName, string surname, DateTime dateOfBirth)
    {
        FirstName = firstName;
        Surname = surname;
        DateOfBirth = dateOfBirth;
    }

    public string FirstName { get; set; }
    public string Surname { get; set; }
    public string FullName => $"{FirstName} {Surname}";
    public DateTime DateOfBirth { get; }
}

public class Employee : Person
{
    public Employee(string firstName, string lastName, DateTime dateOfBirth, DateTime dateOfHire)
        : base(firstName, lastName, dateOfBirth)
        => DateOfHire = dateOfHire;

    public DateTime DateOfHire { get; }
    public Person Spouse { get; set; }
    public bool IsMarried => Spouse != null;
}

通过此实现,IsMarried 属性 仅预测是否设置了 Spouse 属性。这纯粹是为了方便起见,但通常会有帮助。


可能对这些对象有意义的一些验证如下:

public class PersonValidator : AbstractValidator<IPerson>
{
    public PersonValidator()
    {
        RuleFor(p => p.FirstName).NotEmpty();
        RuleFor(p => p.Surname).NotEmpty();
        RuleFor(p => p.DateOfBirth).SetValidator(new DateOfBirthValidator());
    }
}

public class EmployeeValidator : AbstractValidator<IEmployee>
{
    private static readonly DateTime CompanyInceptionDate
        = DateTime.Today.Subtract(TimeSpan.FromDays(365.25d * 10d));

    public EmployeeValidator()
    {
        // Person rules
        RuleFor(p => p.FirstName).NotEmpty();
        RuleFor(p => p.Surname).NotEmpty();
        RuleFor(p => p.DateOfBirth).SetValidator(new DateOfBirthValidator());

        // Employee rules
        RuleFor(e => e.DateOfHire).SetValidator(
            // Can't be in the future nor older than the company
            new DateRangeValidator(CompanyInceptionDate, DateTime.Today));
    }
}

// This class really isn't necessary
// The built-in InclusiveBetween/ExclusiveBetween validators would work just as well
public class DateRangeValidator : PropertyValidator
{
    protected const double DaysInAYear = 365.25d;

    public DateRangeValidator(DateTime from, DateTime to)
        : base($"{{PropertyName}} out of range. Expected between {from:yyyy-MM-dd} and {to:yyyy-MM-dd}.")
    {
        From = from;
        To = to;
    }

    public DateTime From { get; }
    public DateTime To { get; }

    protected override bool IsValid(PropertyValidatorContext context)
        => context.PropertyValue is DateTime date
            ? date >= From && date <= To
            : false;
}

public class DateOfBirthValidator : DateRangeValidator
{
    private static readonly TimeSpan OneHundredAndFiftyYears
        = TimeSpan.FromDays(DaysInAYear * 150d);

    public DateOfBirthValidator()
        // Can't be in the future nor older than 150 years
        : base(DateTime.Today.Subtract(OneHundredAndFiftyYears), DateTime.Today) { }
}