I'm building an MVC Framework application at the moment and I wanted a simple, easy to grep way of validating fields passed back from forms. In MVC-land all the form variables are passed as parameters to the action method. Extension methods turned out to be a very neat solution for doing validation and data conversion. Here's an example:
[ControllerAction]
public void UpdateContact(
int contactId,
string name,
string address1,
string address2,
string address3,
string county,
string postcode,
string telephone,
string email)
{
try
{
name.Label("Name").IsRequired();
address1.Label("Address 1").IsRequired();
county.Label("County").IsRequired();
postcode.Label("Postcode").IsRequired().IsPostcode();
telephone.Label("Telephone").IsRequired().IsTelephoneNumber();
email.Label("Email").IsRequired().IsEmail();
}
catch (ValidationException exception)
{
ContactViewData viewData = ContactViewData(contactId);
viewData.ErrorMessage = exception.Message;
RenderView("Contact", viewData);
return;
}
// update the contact and render the view
}
As you can see we pass the contact's details from a form. In the try block the extension method validators are called. Any of them can raise a validation exception which is caught by the catch block and a view is rendered showing the validation error.
The 'Label' extension returns a ValidateField instance that can be consumed by any other validator, this is so that we can raise exceptions that can be displayed directly to the user:
public static ValidateField Label(this string value, string label)
{
return new ValidateField { Value = value, Label = label };
}
The 'IsRequired' extension takes a ValidateField and checks that the value is not null or empty:
public static ValidateField IsRequired(this ValidateField validateField)
{
if (string.IsNullOrEmpty(validateField.Value))
{
throw new ValidationException(string.Format("{0} is required", validateField.Label));
}
return validateField;
}
And finally the 'IsEmail' extension uses a Regex to validate the string value:
public static ValidateField IsEmail(this ValidateField validateField)
{
// ignore is null or empty, use IsRequired in parrallel to check this if needed
if (string.IsNullOrEmpty(validateField.Value)) return validateField;
string patternLenient = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
if (!Regex.Match(validateField.Value, patternLenient).Success)
{
throw new ValidationException(string.Format("{0} must be a valid email address", validateField.Label));
}
return validateField;
}
I'm finding extension methods a real boon for writing DSL-ish APIs. I'll leave the 'IsTelephone' and 'IsPostcode' exercises for your enjoyment :)