Click here to Skip to main content
Click here to Skip to main content

Validating primitive types with Extension Methods

By , 10 Jul 2012
 

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:

  • bool CanBeNull = true

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

RCoate
Australia Australia
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generallooks interesting ... minor feedbackmemberBillWoodruff10 Jul '12 - 14:44 
I think the code needs cleaning up some, and I suspect it could be refactored, but, please, keep going Smile | :)
 
One minor comment: if the AU date format is dd/mm/yyyy: are you sure that's the format your function is returning when you call:
var myDate = new DateTime(year, month, day);
I don't see any call to the culture-specific provider in that code, if the boolean 'MustBeAnAusDate' == true. Suggest you comment that section.
 
If 'CanBeNull' is true, and the evaluation string is null: can't you exit the function right there: returning 'true ?
 
You might consider moving your RegEx expression out of the function where it is called every time, pre-compile it, if that's possible within the static class structure you are using.
 
Perhaps create the 'Provider once, outside the function, and re-use it ?
 
Finally, picky-picky, I think the choice of the name 'val for string to be validated is not communicative, or mnemonic, and its not consistent with your naming syntax for the other variables. I'd suggest something like "EvaluationString," but it's my bias to like long variable names Smile | :)
 
best, Bill
The glyphs you are reading now: are place-holders signifying the total absence of a signature.

GeneralRe: looks interesting ... minor feedbackmemberRCoate10 Jul '12 - 16:44 
Hi Bill,
Thanks for your feedback. I have refractored a bit and am just uploading a new version of the class now.
 
I've :
Declared a private static readonly provider outside the string validator;
Changed the MustBeAnAusDate to use DateTime.ParseExact;
Created an IsRegExMatch private static method and used that for both MustBeAnEmailAddress and regEx validation rules;
Changed val to EvaluationValue throughout;
 
I've also refractored the CanBeNull and CanBeEmpty rules to:
if (string.IsNullOrEmpty(EvaluationValue))
{
    return EvaluationValue == null ? CanBeNull : CanBeEmpty;
}
Which I think covers every thing in one test (two if you count the ternery function).
GeneralMy vote of 2memberGregory.Gadow10 Jul '12 - 3:46 
Interesting idea, but your article is just a description of your library, with no information on the code behind it. It would be nice if you had taken at least one example and shown us how it had been implemented.
 
I would have given you a 3 for that. However, your download is a single .cs file and when I open it in VS 2008, there are 28 errors all saying "Default parameter specifiers are not permitted." If you had provided a complete project, it would tell me which version of VS I should use. Without even that basic meta information, the source code isn't much use.
GeneralRe: My vote of 2memberErich Ledesma10 Jul '12 - 4:20 
I'm not gonna vote yet but I agree, we need more.
 
This idea looks promising.
 
Greetings
GeneralRe: My vote of 2memberRCoate10 Jul '12 - 12:36 
Thanks for your feedback Gregory.
 
I have updated the article to explain what is in the downloadable code and also that it requires dot net 3.5 or greater. Extension methods, optional parameters and named parameters are not part of the 2.0 framework.
 
Due to my inexperience with the current publishing process for code project articles, this was made live a bit before I was ready. Based on other feedback, I was going to turn it into a "Tip" rather than a full article.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 10 Jul 2012
Article Copyright 2012 by RCoate
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid