Freigeben über


Functional Validation

Since posting a few of my last ideas, my brain has been running a few background threads on how else we can utilize the functional programming when developing applications for .NET Compact Framework (and desktop as well) and take advandage of the power of the LINQ to improve the readability and possibly performance of our applications. So here is another one.

Everybody who develops applications that involve getting some data from a user knows that the data which is coming from the user must be thoroughly validated in order to avoid having "dirty" data in the databases. So we have been creating some validation procedures that would go through the controls on the form, validate the data in them and either display an error to a user or pass it through to a data layer. What if we could utilize the delayed execution of the anonymous delegates to set the validation rules before hand and then apply them when it's needed? Let's consider a typical scenario of validating a numeric only input in some TextBox that you may have on your form and see my ideas in action:

Validator validator;

public Form1()

{

     InitializeComponent();

    

     SetValidationRules();

}

private void SetValidationRules()

{

  validator = new Validator();

     validator.AddRule<TextBox>("Numeric", u => IsNumeric(u.Text) );

}

private bool IsNumeric(string text)

{

     bool result = true;

     try

     {

         Double.Parse(text, System.Globalization.NumberStyles.Any,

                      System.Globalization.NumberFormatInfo.InvariantInfo);

     }

    catch (Exception ex)

     {

         result = false;

     }

     return result;

 }

In the code above we set a validation rule in the SetValidationRules method in which we utilizing the IsNumeric method. When calling the AddRule method on the Validator class we can make use of the lamda expression syntax. After we are done with the rules we can employ these rules to validate an input in the TextBox:

private void txtPin_Validating(object sender, CancelEventArgs e)

{

    if (!validator.Validate<TextBox>("Numeric", txtPin))

    {

        MessageBox.Show("The pin mush be numeric");

    }

}

So we call on to the Validate method of the same Validator class by passing the name of the rule and an instance of the TextBox to be validated. Not bad. Better yet, we should be able to provide a method that would be called when out validation method is executed. Let me illustrate what I am talking about:

// Add another rule

validator.AddRule<TextBox>("Empty", u => u.Text == "", new Action<bool>(ExecuteOnValidate));

 

private void ExecuteOnValidate(bool result)

{

    if (result)

   {

        MessageBox.Show("TextBox is empty.");

    }

}

In the code above we are adding another rule that checks if the Text property of the TextBox is empty, passing an instance of the txtUserName and a handler for the Actition delegate: ExecuteOnValidate. Now when running this validation rule we can write the code:

validator.ValidateWithResult<TextBox>("Empty", txtUserName);

Are you interested in looking at how the Validator class may be implemented? Let's start with the IRule interface and the Rule class implements this interface:

public interface IRule

{

     bool Validate();

     void ValidateWithResult();

}

public class Rule<T> : IRule

{

     private T instance;

     private Func<T, bool> ruleDelegate;

     private Action<bool> outDelegate;

     public Rule(T instance, Func<T, bool> ruleDelegate)

     {

          this.instance = instance;

          this.ruleDelegate = ruleDelegate;

     }

     public Rule(T instance, Func<T, bool> ruleDelegate,

Action<bool> outDelegate)

     {

          this.instance = instance;

          this.ruleDelegate = ruleDelegate;

          this.outDelegate = outDelegate;

  }

     #region IRule Members

     public bool Validate()

     {

          return this.ruleDelegate(instance);

     }

     public void ValidateWithResult()

     {

          this.outDelegate(this.ruleDelegate(instance));

     }

     #endregion

    public T Instance

     {

         get

         {

             return instance;

         }

         set

         {

             instance = value;

  }

     }

}

The Rule class is just a helper class that we are going to use in the Validator class:

public class Validator

{

    // Cache for rules

     private Dictionary<string, IRule> rulesList;

     public Validator()

     {

         // Create an instance of the cache

         this.rulesList = new Dictionary<string, IRule>();

     }

     public void AddRule<T>(string key, Func<T, bool> rule)

     {

         // Add rule to the cache

         this.rulesList.Add(key, new Rule<T>(default(T), rule));

     }

     public void AddRule<T>(string name, Func<T, bool> rule,

Action<bool> result)

     {

          // Add rule to the cache

          this.rulesList.Add(name, new Rule<T>(default(T), rule, result));

     }

     public bool Validate<T>(string name, T instance)

     {

         // Retrieve the rule

         Rule<T> rule = (Rule<T>)this.rulesList[name];

         // Assign the instance

         rule.Instance = instance;

         // Call validate

         return rule.Validate();

     }

     public void ValidateWithResult<T>(string name, T instance)

     {

          // Retrieve the rule

          Rule<T> rule = (Rule<T>)this.rulesList[name];

          // Assign the instance

          rule.Instance = instance;

          // Call validate

          rule.ValidateWithResult();

     }

}

In the AddRule methods in the code above we create an instance of the Rule class and them to the rulesList dictionary. In the Validate methods we retreive an instance of the Rule class and call on its Validate method. Nothing much to it. I am attaching the whole test project to this post. It's a device project, but surely you could use the same code on the desktop. Please consider this code as a sample which has not been tested.

ValidationRulesProject.zip

Comments