2

I've created a custom ASP.NET MVC ValidationAttribute and I'm trying to set my own default error message on it upon instantiation by passing a string to the base constructor:

public class BusinessLogicRegex : ValidationAttribute, IClientValidatable
{
    private const string _defaultErrorMessage = "Invalid Password. {0}";
    private string _description;
    //Other private members
    ...

    public BusinessLogicRegex(string getMember, Type getMemberType, string descriptionMember, Type descriptionMemberType)
        : base(_defaultErrorMessage)
    {
        //Omitted the guts of initializing validation for brevity
        ...
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(ErrorMessage, _description);
    }
}

But when FormatErrorMessage is called, ErrorMessage is null. Why doesn't this base(_defaultErrorMessage) set the ErrorMessage property and how should I set it while still giving the user of this attribute the ability to override it?

Edit - 2nd, cleaner Example:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class TestValidator : ValidationAttribute
{
    public TestValidator()
        : base("Test Error on {0}")
    {
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(ErrorMessage, name);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }
}

Here again, FormatErrorMessage throws a null reference exception because ErrorMessage is null. However ErrorMessageString == "Test Error on {0}". So my mistake seems to be in thinking that calling base("Test Error on {0}") sets the ErrorMessage property. I don't understand this at all. Here how the base constructor describes its errorMessage parameter:

The error message to associate with a validation control

Here is what MSDN says about the ErrorMessageString property:

The error message string is obtained by evaluating the ErrorMessage property or by evaluating the ErrorMessageResourceType and ErrorMessageResourceName properties. The two cases are mutually exclusive. The second case is used if you want to display a localized error message.

Yet inside of FormatErrorMessage at runtime, ErrorMessage, ErrorMessageResourceType and ErrorMessageResourceName are all null. So that description makes it sound like ErrorMessageString should also be null to me. This leaves me very confused about the usage and interaction between all of these properties and the constructor.

8
  • It should be return String.Format(ErrorMessageString, name); Commented Aug 25, 2017 at 22:43
  • No, I want to display _description there. I should put name in the default message somewhere though. But it's ErrorMessage that is null. Commented Aug 25, 2017 at 22:47
  • Which is exactly what that will do! Try it Commented Aug 25, 2017 at 22:47
  • Sorry, maybe I made it confusing by omitting code. _description is declared as a private instance member and initialized int he constructor. See my edit. The purpose of my validator is to go get a regular expression and a description of that expression from some specified class and method so that I can have it use the exact same validation expression that my business logic uses internally. I tried to only include what was relevant to the problem. Commented Aug 25, 2017 at 22:52
  • 1
    Because ErrorMessage is the value that your assign in [Test(ErrorMessage = "...")]. Again, when you use : base("Test Error on {0}") - its assigns that ErrorMessageString Commented Aug 26, 2017 at 8:49

1 Answer 1

2

So I looked at the source code and I see why it's confusing. The ValidationAttribute class maintains a private _defaultErrorMessage field of its own. When I call base(errorMessage) it forwards my string as a lambda to the constructor that accepts a Func<string> errorMessageAccessor and sets that to a private _errorMessageResourceAccessor field. The ErrorMessageString getter first calls a method named SetupResourceAccessor where it determines whether to use

The ErrorMessage property is supported by a private _errorMessage field that appears to only be set by the ErrorMessage property setter. If _errorMessageResourceAccessor is null (which it is not in my case because I set it in the base constructor), this method will decide whether to set _errorMessageResourceAccessor to the default error message, a localized resource message, or the ErrorMessage property. However, if it is not null, it will leave _errorMessageResourceAccessor set to its current value. This is the case I am hitting. Lastly, the ErrorMessage property is supported by an _errorMessage field which appears to only get set by the ErrorMessage setter. The getter will return the default error message if ErrorMessage has not be explicitly set. Once the setter is called, it clears _errorMessageResourceAccessor.

So to try to summarize, calling ErrorMessage will give you the explicitly set error message or the default error message (which I actually don't know where it gets set. Maybe by an inheriting class?). Calling ErrorMessageString will give you a message in what looks to be the following order of presedence:

  • The explicitly set ErrorMessage
  • Localized value from ErrorMessageResourceName if it and ErrorMessageResourceType are set
  • Constructor errorMessage string
  • Default error message if nothing else is set
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.