Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dynamic Validation with FluentValidation - Alternative

0.00/5 (No votes)
10 Jan 2016 1  
This is an alternative for Dynamic Validation with FluentValidation in WPF/MVVM

Introduction

FluentValidation seems to be a very convenient way to handle ValidationRules in Wpf.

I really recommend to read the Original-Article by tetsushmz, which gives good introduction.

Application-Summary

There is a Viewmodel, named "Problem5" (a Math-Puzzle, see Problem5 on Project Euler..net), in a separate Project, where the user can input two texts, and then raise a command to calculate the result.

Validating theese Texts is not that trivial, and FluentValidation does a good job on it.

And the View demonstrates, how invalid textboxes automatically get marked, and the button automatically gets disabled in case of ocurance of errors.

Moreover the MainViewmodel provides a List of error-messages of all invalid properties of the Problem5-Instance.

What I did different

In general I left the original Solution as it was, but I simplified the Usage, and I could remove an Interface and I reduced the number of Members which are needed to make FluentValidations work.

Here the usage-obligatorals, as shown in tetsushmz' approach

public class Problem5 : ValidatableBindableBase, INotifyDataErrorInfo {

   private readonly IValidator<Problem5> validator;

   public IEnumerable GetErrors(string propertyName) {
      return this.validator.GetErrors(propertyName);
   }

   public IList<string> GetAllErrors() {
      return this.validator.GetAllErrors();
   }

   public override void ValidateAllProperties() {
      this.validator.Validate(this);
   }

   public Problem5(IValidator<Problem5> validator) {
      this.validator = validator;
      this.validator.ErrorsChanged += (s, e) => this.OnErrorsChanged(e);
      // ...
  1. Inherit from ValidatableBindableBase, implement INotifyDataErrorInfo
  2. Declare a validator-Object
  3. Implement INotifyDataErrorInfo.GetErrors(), and redirect it to the validator
  4. Implement INotifyDataErrorInfo.GetAllErrors(), and redirect it to the validator
  5. Override ValidatableBindableBase.ValidateAllProperties(), and redirect it to the validator
  6. Inject the validator-Object via Constructor (and store it)
  7. Subscribe the IValidator.ErrorsChanged-Event, to call the ValidatableBindableBase.OnErrorsChanged()-Method

Now my simplification:

public class Problem5 : FluentNotifyDataErrorInfo {

   private readonly AbstractValidator<Problem5> validator;

   protected override ValidationResult Validate() { return this.validator.Validate(this); }

   public Problem5(AbstractValidator<Problem5> validator) {
      this.validator = validator;
      //...
  1. Inherit from FluentNotifyDataErrorInfo
  2. Declare a validator-Object
  3. Override FluentNotifyDataErrorInfo.Validate(), and redirect it to the validator
  4. Inject the validator-Object via Constructor (and store it)

Memory-Footprint

Usually Validation concerns many Objects, or even large amounts of Data. So it's a good habit, to keep the validations memory-footprint small.

In the original-concrete Implementation of IValidator<Problem5> tetsushmz allocates for each Data-Item its own Dictionary, and it's own Problem5Validator, an object, which holds all the Validation-Rules.

This is not necessarily necessary Wink | ;-) .

My Implementation broadcasts the same Problem5Validator-Instance to all Data-Items, and my FluentNotifyDataErrorInfo (Data-Items Base-Class) only contains an Error-Dictionary, if Errors are present - otherwise it is set to a static empty Dictionary.

Drawbacks of my Alternative

  • My Data-Item-Assembly needs to reference the FluentValidation-Library. I decided to dispense the discoupling IValidator<T>-Interface of the original, to get things simpler.
    Since that Library is needed anyway in a fluent-validated Application, no matter, whether the Data-Item-Assembly refers it or not.
  • Some scenarious may require, that each Data-Item holds its own Set of Validation-Rules. I cannot imagine such szenariouses, but I must accept the possibility of that.

Sidenote: Difference-Analyse-Algorithm

One (small) Challenge to master is, to compare the previous error-list with a newer version, to detect, which errors are vanished, which remained the same, which were changed and which are new.

Difference-Analyse is a general pattern, and here is a reciepe, to solve such performantly:

  1. convert list1 to a dictionary or a hashset - name it dic1.
  2. then loop list2, and try remove each item2 from the dic1. Hashset/Dictionaries Remove()-Function returns False, if the item isn't present
  3. If True: both Items are same - means: That Item is unchanged
  4. If False: That item2 is new
  5. After having looped all item2s, the remaining items (if present) in dic1 are the deleted ones - since in list2 they were not present.

This reciepe is meant to be adapted to the current scenario - if you like, look at my FluentNotifyDataErrorInfo.ProcessChangedErrors() - Method:

  1  private void ProcessChangedErrors(Dictionary<string, string> updatedErrors) {
  2     var oldErrs = this._Errors;
  3     _Errors = updatedErrors;
  4     foreach (var kvp in updatedErrors) {
  5        string oldErr;
  6        if (oldErrs.TryGetValue(kvp.Key, out oldErr)) {
  7           oldErrs.Remove(kvp.Key);
  8           if (oldErr == kvp.Value) continue;
  9        }
 10        // raise on new error or on changed error-message
 11        ErrorsChanged.Raise(this, new DataErrorsChangedEventArgs(kvp.Key));
 12     }
 13     // raise, if old error is vanished
 14     foreach (var oldErrKey in oldErrs.Keys) 
 15        ErrorsChanged.Raise(this, new DataErrorsChangedEventArgs(oldErrKey));
 16  }

Its Job is to Raise the INotifyDataErrorInfo.ErrorsChanged-Event for each Property, on which the presence (or only the error-Message) of an error did change.
I had to alter the reciepe above in the detail, that I used Dictionary.TryGetValue() instead of Dictionary.Remove(), because an additional Check is required to detect, whethr it is changed or not (line #8).

Conclusion

Some people may see me as an unpleasant smart-ass, and I'm usure, whether I could reject that convincingly Wink | ;-) . But does that really matter? If my unpleasant property helps you to work better with validations, then it's not completely unworthy, is it?

And I really think, examining different approaches, which achieve the same result,  is very helpful, to figure out useful patterns, capabilities, flexibilites, pros and cons and stuff.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here