1
\$\begingroup\$

This class validates that a name contains only letters and optionally spaces, hyphens and apostrophes.

public class NameAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null) return ValidationResult.Success;

        string name = value.ToString();
        var regex = new Regex(@"^[a-zA-Z]+(?:['-][a-zA-Z\s*]+)*$");

        return regex.IsMatch(name)
            ? ValidationResult.Success
            : new ValidationResult($"The name '{name}' is invalid, it should consist of only letters, and optionally spaces, apostrophes and/or hyphens.");
    }
}

One thing I wasn't sure on was the null check at the start, we've got the Required attribute if something is required and I don't want the validation to occur if the value is null. Is this an appropriate way of handling it?

\$\endgroup\$
1
\$\begingroup\$

From the separation of concerns perspective it seems fine that you do not want to validate against emptiness. On the other hand, one can argue that why do you treat an empty string as a valid name.

My suggestion is to create a ctor with a parameter, where the consumer of the attribute can define how should it behave in case of empty input.

public class NameAttribute : ValidationAttribute
{
    private static readonly Regex NameRegex = new Regex(@"^[a-zA-Z]+(?:['-][a-zA-Z\s*]+)*$", RegexOptions.Compiled);
    private readonly bool shouldTreatEmptyAsInvalid;
    public NameAttribute(bool shouldTreatEmptyAsInvalid = true)
    {
        this.shouldTreatEmptyAsInvalid = shouldTreatEmptyAsInvalid;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null)
            return shouldTreatEmptyAsInvalid 
                ? new ValidationResult($"The name is invalid, it should not be empty.") 
                : ValidationResult.Success;

        return NameRegex.IsMatch(value.ToString())
            ? ValidationResult.Success
            : new ValidationResult($"The name '{value}' is invalid, it should consist of only letters, and optionally spaces, apostrophes and/or hyphens.");
    }
}

I moved the creation of the Regex to a static variable, but you should take some measurements in order to be sure, which has the best performance. Please check this MSDN article for other alternatives.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks, so with that in mind, it would negate the need for a Required attribute, is that right? \$\endgroup\$
    – MF1010
    Jun 25 '20 at 12:25
  • \$\begingroup\$ @MF1010 With this version you are able to use this with / without the Required attribute. So your logic does not depend on the existence of an other attribute. \$\endgroup\$ Jun 25 '20 at 12:28
  • 1
    \$\begingroup\$ Thanks, that makes sense. \$\endgroup\$
    – MF1010
    Jun 29 '20 at 7:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.