0

ValidationAttribute.ErrorMessage is a string implying there should be one error per ValidationAttribute.

Say I have some code like this:

[BeforeThan(nameof(EndTime), nameof(EndTime2), ErrorMessage = "StartTime should before than EndTime and EndTime2")]
public DateTime StartTime { get; set; }

and an IsValid method like this:

protected override ValidationResult IsValid(object startTime, ValidationContext validationContext)
{
    var endTimePropertyValue = validationContext.ObjectType.GetProperty(EndTimePropertyName)
          .GetValue(validationContext.ObjectInstance);

    if (startTime != null && startTime is DateTime
        & endTimePropertyValue != null && endTimePropertyValue is DateTime)
    {
        if ((DateTime)startTime > (DateTime)endTimePropertyValue)
        {
            return new ValidationResult(ErrorMessage);
        }
    }

    return ValidationResult.Success;
}

There is unobtrusive JavaScript on the client side.

Say I wanted to add more logic to say; if end date 1 is the same as end date 2 then report a different error message.

  1. Create a separate attribute for this i.e. have two attributes.
  2. Amend the code above somehow to return two different error messages depending on the scenario

Please remember that I am usinf obtrusive JavaScript on the client side.

Aleks Andreev
  • 7,016
  • 8
  • 29
  • 37
w0051977
  • 15,099
  • 32
  • 152
  • 329

1 Answers1

0

There is no explicit need of using only ErrorMessage property to set error text. You can introduce additional properties indicate whether it's needed to check if dates are equal

public string ErrorMessage2 { get; set; }

protected override ValidationResult IsValid(object startTime, ValidationContext validationContext)
{
    var endTimePropertyValue = validationContext.ObjectType.GetProperty(EndTimePropertyName)
            .GetValue(validationContext.ObjectInstance);

    if (startTime != null && startTime is DateTime
        & endTimePropertyValue != null && endTimePropertyValue is DateTime)
    {
        DateTime startDateTime = (DateTime)startTime;
        DateTime endDateTime = (DateTime)endTimePropertyValue;

        //if second error message is not empty we check if date are the same
        bool checkIfEqual = !string.IsNullOrEmpty(ErrorMessage2);

        if (checkIfEqual && startDateTime == endDateTime)
        {
            return new ValidationResult(ErrorMessage2);
        }
        else if (startDateTime > endDateTime)
        {
            return new ValidationResult(ErrorMessage);
        }
    }

    return ValidationResult.Success;
}

Or you can discard ErrorMessage at all and use hardcoded strings

private const string StartDateBefore = "StartTime should before than EndTime and EndTime2";
private const string StartDateEqual = "StartTime is equal to EndTime";

public bool CheckIfEqual { get; set; }

protected override ValidationResult IsValid(object startTime, ValidationContext validationContext)
{
    var endTimePropertyValue = validationContext.ObjectType.GetProperty(EndTimePropertyName)
            .GetValue(validationContext.ObjectInstance);

    if (startTime != null && startTime is DateTime
        & endTimePropertyValue != null && endTimePropertyValue is DateTime)
    {
        DateTime startDateTime = (DateTime)startTime;
        DateTime endDateTime = (DateTime)endTimePropertyValue;

        if (CheckIfEqual && startDateTime == endDateTime)
        {
            return new ValidationResult(StartDateEqual); //error message when dates are equal
        }
        else if (startDateTime > endDateTime)
        {
            return new ValidationResult(StartDateBefore); //error message when start date is after enddate
        }
    }

    return ValidationResult.Success;
}

Usage

[SomeValidation(nameof(EndDate), CheckIfEqual = true)]
public DateTime StartDate { get; set; }

To make this validation attribute work with client side validation you need to implement IClientModelValidator interface as it described here.

public void AddValidation(ClientModelValidationContext context)
{
    //"beforedate" and "equaldate" will be added as custom validators
    //for unobtrusive validation
    context.Attributes.Add("data-val-beforedate", StartDateBefore);

    if (CheckIfEqual)
        context.Attributes.Add("data-val-equaldate", StartDateEqual);
}

With this code implemented input will contain additional attributes with respective error messages. Now we need to implement custom validators in javascript and copy validation logic from C# code

//add "beforedate" custom validator
$.validator.addMethod("beforedate", function (value, element, parameters) {
    var startDate = new Date(value);
    var endDate = new Date($("#EndDate").val());

    //if condition is true then value considered valid
    return endDate >= startDate;
});

//add unobtrusive adapter to run "beforedate" validation
$.validator.unobtrusive.adapters.add("beforedate", [], function (options) {
    options.rules.beforedate = {};
    options.messages["beforedate"] = options.message; //save error message passed from C#
});

$.validator.addMethod("equaldate", function (value, element, parameters) {
    var startDate = new Date(value);
    var endDate = new Date($("#EndDate").val());

    return endDate.getTime() != startDate.getTime();
});

$.validator.unobtrusive.adapters.add("equaldate", [], function (options) {
    options.rules.equaldate = {};
    options.messages["equaldate"] = options.message;
});
Alexander
  • 9,104
  • 1
  • 17
  • 41