Introduction
Using extension methods can be useful to validate data when you are dealing with untrusted input from various sources (like user entered data on a web page or importing CSV files from an external FTP source).
The download code
The BasicValidator.zip file contains a single static class, with a number of static boolean methods. Each method is an extension of a primitive data type that adds a Validate method to it.
The code requires dotnet 3.5 or higher as it uses optional parameters, named parameters and extension methods (which were only introduced in 3.5).
The BasicValidator class is written in c#, but can be fairly easily translated to VB.Net. Let me google that for you.
Background
I recently found myself in a position where I needed to do a bunch of server side validation for our corporate website. I also knew that the very next project in line was a task engine to process externally generated pdf files and place them into our records management system. In this case, all of the needed meta data would be embedded in the file name of each file.
In order to minimise the amount of validation code for these projects, I came up with an extension method class to handle the most simple cases. All of the data types I would be dealing with would be integers, strings, dates or bools, so my library only deals with these types. It should be fairly trivial to add other types when they are needed.
One other desired outcome was readability. When I look at this code in two years time, I want to be able to easily identify what my validation rules are without having to trace through code or even refer back to the validation library.
Using the code
There are six methods in the validation class. One for each of:
- Nullable int
- Int
- String
- Nullable datetime
- Datetime
- Nullable bool
Note: Nullable bool and nullable int are there due to a quirk with out CRM system, where all data types are nullable, but often our business rules require an actual value
Each method makes use of named parameters with default values, so using any of the methods is fairly trivial
Nullable Integer
Available Rules:
- bool CanBeNull = true
- int MinVal = int.MinValue
- int MaxVal = int.MaxValue
Example:
int? untrustedData = 42
if(! untrustedData.Validate(CanBeNull:false, MaxVal:40){
return "Sorry, data out of range";
}
Integer
Avaliable Rules:
- int MinVal = int.MinValue
- int MaxVal = int.MaxValue
Example:
int untrustedData = 42
if(! untrustedData.Validate(MinVal:50){
return String.Format("Sorry, {0} is not a valid number. Min value is 50", untrustedData);
}
string
Available Rules:
- string regEx = null
- bool CanBeNull = true
- bool CanBeEmpty = true
- int MaxLength = int.MaxValue
- string MustStartWith = null
- string MustContain = null
- string MustEndWith = null
- bool MustBeAnInt = false
- bool MustBeAnAusDate = false
- bool MustBeAnEmailAddress = false
Notes: The CanBeNull
and CanBeEmpty
rules are processed first. If they are left at their default value (true) and the string is null or empty, the validator will return true regardless of any other rules. This behaviour is by design. If you want to check that a string has a minimum length, and/or matches a regEx and/or any other rule AND is not null or empty, make sure you set both CanBeNull
and CanBeEmpty
to false. Without these settings, what you are checking for is "does this string match my rule(s) OR is it null Or is it an empty string".
The regEx
rule takes a regular expression and returns true if the string value matches the regEx. It is up to you to make sure your regEx works.
The MustBeAnAusDate
attempts to match a "dd/mm/yyyy" date patern. Feel free to add a US (or any other culture) date test.
The MustBeAnEmailAddress
uses a regEx pattern of "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$" to attempt to verify that the string is a valid email address. Again, feel free to change this to whatever regEx takes your fancy.
Example:
var untrustedData = "Some.Email@Address"
if(!untrustedData.Validate(CanBeNull:false, MustBeAnEmailAddress:true){
return string.Format("{0} is not a valid email address", untrustedData);
}
if(!untrustedData.Validate(MaxLength:10){
return string.Format("{0} is too long, maximum length is 10 characters", untrustedData);
}
Nullable DateTime
Available Rules:
- DateTime? MinDate = null
- DateTime? MaxDate = null
- bool CanBeNull = true
Example:
var untrustedData = DateTime.Now;
var yesterday = DateTime.Now.AddDays(-1);
if(!untrustedData.Validate(MaxDate:yesterday){
return string.Format("{0} is not a valid date, must be before {1}", untrustedData, yesterday);
}
DateTime
Available Rules:
- DateTime? MinDate = null
- DateTime? MaxDate = null
Example:
var untrustedData = DateTime.Now;
var tomorrow = DateTime.Now.AddDays(1);
if(!untrustedData.Validate(MinDate:tomorrow){
return string.Format("{0} is not a valid date, must be after {1}", untrustedData, tomorrow);
}
Nullable Boolean
Available rule:
Example:
bool? untrustedData = null;
if(!untrustedData.Validate(CanBeNull:false){
return "Null values are not allowed";
}
Points of Interest
It is posible to use these validation methods without the named parameters if you explicity pass in parameter values up to the rule you are actually interested in. I don't advise this approach as it will make your code much harder to read and maintain.
I had not played with extension methods much before writing this validation class and so was quite pleased with the power it gave me. I was able to simplify and mimimise the validation sections of my code so that I was just dealing with simple rules rather than writing extensive methods.
My approach here is to just return boolean values on each type, but there is no reason not to refractor this to include an out or ref parameter to include user feedback messages when validation fails (if that is something you need).
History
Version 1: 2012-07-10
Uploaded a new version of BasicValidator.cs that has a number of minor improvements. Usage and Method signatures are unchanged: 2012-07-11