CustomRule and access to other method properties.

Topics: Developer Forum, User Forum
Oct 28, 2008 at 5:13 PM
Hello,

The framework is great, but I've noticed some limitations that while can be worked around, are very annoying at times. A perfect example is like the one below:

A common validation requirement for many businesses is for an age to be validated to be at least 18 years old, unless I am going about this the wrong way, this cannot be dynamically achieved without using a Custom Rule because the date it needs to validate against must be dynamic, which cannot be put into the attribute because it requires a constant. So the fix for this is to create a custom rule that takes the value and then does a datetime.compare to validate it...simple enough solution.

Another common validation is, for example, comparing two strings entered to make sure they are the same (in cases like changing your password). The Compare validator doesn't work for something like this because you can't use one parameter to validate another because again, they are dynamic. I can't use the exact same solution as above to correct this though, since with a custom validator, I still only have access to one of the strings, not the other.

I was able to modify the framework to allow for that example to work, and it essentially adds all the method parameter names and their appropriate values to a hashtable, and attaches that hashtable to a new CustomValidationEventArgs property. I believe the intent of this framework was never to cross-validate as the such example, but I can't see why those extra calls should be put into the method when they can still be cleanly handled in a custom rule validator.

This is essentially the "fix" for the above in InternalValidate in ParameterValidationManager.cs
 System.Collections.Hashtable ht = new System.Collections.Hashtable();
            foreach (var parameter in methodDescriptor.Parameters)
            {
                ht.Add(parameter.Name.ToLower(), parameters[parameter.Position]);
            }
...
var isValid = rule.Validate(target, parameterValue, context, null, ht);

Was this a good solution and would this be something that would help others or am I just going about this the wrong way?

Thanks,

Bruce
Coordinator
Oct 29, 2008 at 2:11 PM
Bruce,

I think you have a valid point. I'm in the process of making an architectural change the to the framework. In this change I'll try to include the 'parameter cross validation' you described. I'm thinking of doing something similar to what you have. In theory, rules that needs to do cross parameter validation will be supplied a MethodContext to test. This MethodContext will have a dictionary/hash of parameter name/values as well as any other valuable information. This methodContext will replace the memberValue and targetValue params. Your rule will simply validate the method context as you see fit.

In the case you described you'd no longer have to augment the CustomValidationEventArgs or, what looks like in your example, the rule.Validate method.

Does this sound alright?

As far as cross validating properties. As you pointed out, its pretty easy to achieve with customRule. In the upcoming change I'm considering making a more formal version of a 'property comparison' rule. The example I'm modeling against is a StartDate < EndDate validation. If I can do that simply I'm assuming the other cross property validation will be easy too :).

Regards,
Dane
Oct 29, 2008 at 3:35 PM
Dane,

That sounds perfect. I just modified the EventArgs and rule.Validate because it seemed to be the quickest solution for the current project I'm working on.

I also came up with something else yesterday while working you may or may not want to consider.

In the project i'm working on, localization is an important requirement, so I've written a ErrorMessageProvider to handle the translation depending on the current culture. One thing I've noticed, and this is while I was writing the custom error validators is that depending on the situation, I may want to change what ErrorMessage is returned. A perfect example is the following:

I am validating to see if a credit card number is unique within the system, this requires a database call inside the custom validator, I set the default ErrorMessage to let the user know the credit card already exists...but what if there's an exception in the database call? I have chosen to do two different things depending...

If the application is in a debug mode, it will return the exception text as an error, otherwise it will display another simple error to the user saying something to the effects of "Contact customer service."

In order to do this, I created a method to change the ErrorMessage and UseErrorMessageProvider depending on the situation since I do not see a current way of doing this.

But this arises yet another question or possible problem..If parameters that had multiple validators (say a required validator and a custom validator) would stop validation at the first failed validation. The reason for this is to prevent the custom validator and it's database call from firing if an empty string is passed in. As of this time, the custom validator is also making the null or empty string check, and if it fails, it just sets e.IsValid to true because validation would have already been picked up by the required validator, but this doesn't seem like the best way to go about it.

Regards,
Bruce
Coordinator
Oct 29, 2008 at 6:27 PM
Edited Oct 30, 2008 at 2:22 PM

Bruce,

You bring up 3 interesting points. I'll discuss below.

Note that I refer to the new validator concept in the upcoming change. This replaces Rule. Validators will do the actual testing of a value. Rule hold validator metadata (like a consumer overridden error message, or severity), tell the validator to perform its test, and report its results on failure. There will be more on that in the future though.

Localization: I think the framework definitely needs this feature to be enhanced. We haven't had many requests for it until recently. After I make the change I'm working on, I'll be looking at some ways to make this a little more robust. Right now I think part of the problem with the current implementation is that localized messages are key'd off of messages and not message formats. Also, I think localization should be baked in, not optional. If the localization information exists for a culture why even ignore it? We're totally open for suggestions on this front as we consider the enhancements.

Multiple Error Messages for Validate: This is one I've been struggling with for a little while now. With the upcoming change I've tried to force only 1 default error message per validator. There are some cases, like yours, that cause me to double check my reasoning. Here is the problem: Its a good idea to give validator consumers the ability to override its error message, however, allowing users to override messages creates an awkward situation. Users can only override a single message for the whole validator. Inorder to override multiple messages you'd basically have to recreate the rule (if db conn not present show this error, else show this error, etc.). So, in your DB example, if a consumer decided to override the error message with "Please contact support via abc@xyz.com" you would have no way of bubbling the debug message, which you depend on, to the top. So, it turns out, by giving developers access to the error message at validation time we give them the ability to create rule's that break other features (in this case the message override feature). To combat this going forward, we're planning limiting validators to a single error message. The Validate method will simply return a bool indicating a pass or fail. This should simplify things in many ways - but we want to be sure your still able to do, essentially, what you were doing before.

Let me reframe your UniqueCardValidator example and lets see if this will work for you. Your UniqueCardValidator essentially can only pass in 1 way: we've determined that the card is infact unique. Since it can only pass in 1 way, a failure should mean 1 thing: "The card is not unique" - this would be your error message. The validator may depend on a few things inorder to do its job. One of which, for argument sake, is a live database connection. If the validator doesn't have this can it do its job? No. Can we recover from this? No. In this case we can't say either way whether the card is unique or not, we can't even try the test. For these cases I think an exception should be thrown. I'm planning on having exceptions wrapped in a ValidationException higher up in the framework. But, if we use an exception to communicate the fact the live DB connection wasn't present we solve your problem: You have to localize only 1 error message and you communicate the reason for a failure via the exception. Exception can be caught to show the user a message saying "Contact Customer Service" or, if in debug mode, show its details.

Does that make sense? If you were forced to only have 1 message per validator and exceptional cases were handled this way, would you be able to cover alll your usecases?

If you have any other examples where multiple error messages are required please send them over. I really want to make this simplification where consumers don't build validation results by hand, but if I can't I can't.

Priority: The way I look at this is that a validator should never care about what other validators are applied to a given member. In the example with parameters, it seems like custom validator is assuming there is a required validator- which should be avoided. Really the custom validator should be responsible for itself. So if it needs a string and didn't get one, it should throw an exception. However, this is not always good, as I think you indicated, since the exception isn't needed if we checked other validators first. If we're checking a parameter that has 2 validators we'd want to check FIRST the required validator (which won't throw an exception), stop validation processing, and force the user to fix it before continuing.

To support this, I plan on adding Priorty markers to rules. This way you can define, more a less, an execution order for rules. This should solve that problem.

Thanks for the feedback,
Dane

Oct 30, 2008 at 4:55 AM
Hi Dane,
                 I was just going through your post and noticed that you had mentioned about localization regarding the ValidationFramework,I had posted related to validation in this post.(Some views mentioned).I would be of help if any solution related to this can be done to the framework making it self contained related to validation , this could possible help reduce cross cutting concerns and overhead in a way.

Thanks in advance.
Coordinator
Oct 30, 2008 at 2:24 PM
Hi abm_v,

Yeah I saw your post come across a bit ago. I have some idea's to discuss. I'll post them in the other thread when I get a chance. We'll see if we can brainstorm how to make this localization feature more robust :)