So I am going to deviate from my previous blog which was a brief introduction to Angular.js towards some server side code I did over the weekend. It was a ‘code’ weekend for me and I pretty much spent each day learning something new. It was fun, and exhausting! Anyways, what I am sharing today is how to make custom Data Annotation Attributes!
In an ASP.NET MVC project, we often like to decorate our model and view model classes with attributes like [Required] or [MaxLength(10)] and so on. These are really helpful as we can use them to form validation logic on our views (and the ModelState) without ever touching our views! Adding the [Required] data annotation attribute to a view model will prevent the user from leaving an input blank that is linked to that property of our model. Scaffolding will even take care of the jqueryval and html for us. All by simply adding an attribute to a property we can enforce changes all the way up to our views. Pretty neat!
Well, here at Palm Beach Software Design, we like reusable code. Being able to create custom Data Annotation attributes could really be useful. So, I created a simple annotation called ExcludeChar that accepts a string. What this attribute does is examine the incoming value, say an html input field, and checks to see if it contains any character in the string that was passed to the attribute. If it does, we return a ValidationResult with a message we would like to send to notify the user why their value is wrong!
So, if we don’t want symbols being passed from a view to a model and then eventually to our database, one thing we can do is to prevent the model property with [ExcludeChar(“@{^”)] and my modelState will return false for its IsValid property and in returning the view MVC scaffolding automatically takes care of the validation summary for me.
Let us walk through how to create a custom data annotation attribute briefly. First, we need to create a class that inherits the ValidationAttribute class which requires a reference to System.ComponentModel.DataAnnotations. If we look at its definition we will find several different properties and methods, but the ones of interest for this blog will be the ErrorMessage property, and the virtual IsValid method that returns a ValidationResult. Well, because we inherited this class, we can override the IsValid method! Below is what we see when looking at the ErrorMessage property and the virtual ValidationResult IsValid method.
Summary:
// Gets or sets an error message to associate with a validation control if validation
//fails.
//
// Returns:
//The error message that is associated with the validation control.
public string ErrorMessage { get; set; }
//
// Summary:
//Validates the specified value with respect to the current validation attribute.
//
// Parameters:
// value:
// The value to validate.
//
// validationContext:
// The context information about the validation operation.
//
// Returns:
// An instance of the System.ComponentModel.DataAnnotations.ValidationResult
// class.
protected virtual ValidationResult IsValid(object value, ValidationContext validationContext);
Let us then look at the class we made and follow the comments to understand what we have implemented.
//Linq… because Linq is awesome
using System.Linq;
//System.ComponentModel.DataAnnotations is where we have access the the ValidationAttribute class using System.ComponentModel.DataAnnotations;
namespace MyGarageSale.Helpers.DataAnnotations
{
//create the class and inherit from Validation Attribute
public class ExcludeChar : ValidationAttribute
{
private string _chars;
//Chars is the string value that will be used to validate the users input
public ExcludeChar(string Chars)
: base()
{
this._chars = Chars;
}
//value is the value passed by the user
protected override ValidationResult IsValid(object value, ValidationContext valicationContext)
{
if (value != null)
{
//Here is where I check to see if the user inputs value contains any character in the string that was passed to the ExcludeChar attribute
char[] invalidChars = _chars.ToCharArray().Distinct().ToArray();
string valueAsString = value.ToString();
string displayInvalidChars = “”;
bool modelError = false;
for (int i = 0; i < invalidChars.Length; i++)
{
//if value contains a character from the invalidChars array I set modelError to true, and store the invalid character in displayInvalidChars
//this is so I can inform the user of which characters in the input is invalid
if (valueAsString.Contains(invalidChars[i]))
{
modelError = true;
displayInvalidChars += invalidChars[i].ToString();
}
}
if (modelError)
{
//this is where we simply form and return the error message
string InvalidCharesForErrorMessage = “”;
for (int i = 0; i < displayInvalidChars.Length; i++)
{
if (i == 0)
{
InvalidCharesForErrorMessage += displayInvalidChars[i].ToString();
}
else
{
InvalidCharesForErrorMessage += “, ” + displayInvalidChars[i].ToString();
}
}
string plural = “”;
if (InvalidCharesForErrorMessage.Length > 1)
{
plural = “s”;
}
string errorMessage = string.Format(“{0} must have valid characters. Invalid character{2}: {1}”, valicationContext.DisplayName, InvalidCharesForErrorMessage, plural);
return new ValidationResult(errorMessage);
}
}
if all goes well, modelError will be false and we will return a ValidationResult.Success
return ValidationResult.Success;
}
}
}
So there you have it! A simple and lightweight reusable class, and with that one line of code can influence the stack from the model up. This can save hours and hours of writing or re-writing server side or even client side code for validation. That means faster development output which is beneficial for everyone. There are many cool things we learned here at Palm Beach Software Design, custom ActionFilter that captures all exceptions and writes them to a database, implementing a Unit Of Work repository class, a little Twitter Bootstrap, etc. that really just seem more awesome to me.
At Palm Beach Software Design, Inc we take the time to determine the best implementation based on your business needs, we strive to create custom software for our customers that will increase profits and productivity.