Question Custom Validation Attributes in .NET Core

Mateycho

Member
Joined
Mar 12, 2024
Messages
10
Programming Experience
3-5
Hi guys, I am really struggling the previous week to find out why is there a difference when I pass the error property through the constructor of my custom validation attribute or when I pass it explicitly. I have been looking through the source code so much that my eyes started hurting, hah. When I pass it through the constructor on the client side I get the following error: [de-CH: isValidEmail], which might be since we have many languages on our application, but when I pass the same string explicitly it returns the correctly formated error.

Can someone explain why we have this behavior? Also, what would be the best practice with validation attributes to override bool isValid and FormatErrorMessage methods or something other, since I don't want to pass the ErrorMessage in the ViewModel to pollute the code there? Thanks!

Currently, I am doing things like in the picture and it works fine, but I am just speechless why doesn't the error message work when passed from the constructor? Could it be because it expects some formating, or culture definition before passing and the ErrorMessage explicitly is like the default one, which doesn't need such modifications to be displayed correctly? Can someone help?
1710226113988.png
 
No we don't have any clues towards why you are getting your behavior. It does close out other avenues of why it shouldn't be showing the behavior you are getting.

It works consistently when set either way based on the code. It is just more efficient when set via the constructor vs when set via the ErrorMessage property. The former sets up internal _errorMessageResourceAccessor directly. The latter initially sets it to null, but SetupResourceAccessor() eventually sets it to the same thing passing in via the constructor did but pulls the value from ErrorMessage.

At this point, I'm thinking that it's possible you are accidentally setting ErrorMessageResourceName instead of ErrorMessage due to the <sarcasm>ever helpful</sarcasm> IntelliSense that tries to auto complete while typing. If ErrorMessageResourceName is set, then you can get the behavior that you are seeing.
 
Last edited:
I also just don't get where is there room for different behavior between setting it in the constructor or explicitly. To both of the strings, no modifications are made and both of them finish/are returned like this:
_errorMessageResourceAccessor = () => localErrorMessage;
 
At this point, I'm thinking that it's possible you are accidentally setting ErrorMessageResourceName instead of ErrorMessage due to the <sarcasm>ever helpful</sarcasm> IntelliSense that tries to auto complete while typing. If ErrorMessageResourceName is set, then you can get the behavior that you are seeing.
Actually in my case setting it via the constructor does NOT work and setting it via ErrorMessage works...
 
I recommend putting together a minimal repro case, and then turning on the debugger options to debug into the .NET Core code. That way you can see step by step to see why it's acting that way.
 
I recommend putting together a minimal repro case, and then turning on the debugger options to debug into the .NET Core code. That way you can see step by step to see why it's acting that way.

Unorunaly I cannot put brakepoints into decompiled .NET Code but I would love to.
 
I was hoping to tell you to just create a function breakpoint (^K,B) at System.ComponentModel.Annotations.dll!System.ComponentModel.DataAnnotations.ValidationAttribute.FormatErrorMessage(string name) , but doesn't seem to work with my current build of VS2022 (17.9.3).

So you'll have to do things the brute force way. In your attribute class, put in this override of the FormatErrorMessage() that will force the debugger to break in, and give you an opportunity to step into the actual .NET Core implementation:
C#:
    public override string FormatErrorMessage(string name)
    {
        Debugger.Break();
        return base.FormatErrorMessage(name);
    }

Next setup your debugging options to let you load the symbols for .NET Core and also debug into it:
1710510010236.png


1710510397839.png


Let us know what you debugging exploration tells you!
 
Back
Top Bottom