Validation is a topic that comes up quite frequently. One concern is how to honor DRY (don't repeat yourself) principles and create a framework that is reusable across layers. There are some very robust and tested frameworks out there, but sometimes they might be more than what you are looking for. In this post, I'll show you how to build a standalone validation library that can be shared between Silverlight and the server, then integrated via MEF and PRISM for fluent validation.
Rules and Violations
First things first. We need to lay down the validation framework. One technique I've learned when working with Silverlight is to build your main classes as Silverlight classes first, so they take advantage of the "lower denominator." Class libraries built in Silverlight are then easily shared on the server - the easiest, "out of the box" way is to create a mirrored .NET project and link the classes.
I suspect when a rule is violated, it might pertain to more than one property, so I created a rule violation to look like this:
public class RuleViolation { public RuleViolation(string message, IEnumerable<string> properties) { Message = message; Properties = properties; } public string Message { get; private set; } public IEnumerable<string> Properties { get; private set; } }
Next, we need to define what a validation rule looks like. What I want to do is define various rules and tag them with MEF to export, and have a centralized place to request and execute the rules. The interface I decided to go with looks like this:
public interface IValidationRule { IEnumerable<RuleViolation> Validate(string propertyName, object propertyValue); IEnumerable<RuleViolation> Validate(string propertyName, object propertyValue, Dictionary<string,object> parameters); }
We can simply pass a property and a value, possibly with additional parameters, and receive a list of violations. The parameters dictionary is one place some might complain. Instead of having fine-grained validations that each have different signatures, I like to compromise and use an open interface. I can still type it somewhat (I'll show you how) but I prefer the flexibility of requesting the common interface over keeping track of all of the various method signatures.
Defining Rules
To explain what I mean, let's build two validation rules, one that takes parameters, and one that doesn't. Here is a rule that says, "this is required." First, we'll create a base validation class to take care of common tasks so our actual rules are easier to implement:
public abstract class BaseRule : IValidationRule { private readonly List<RuleViolation> _violations = new List<RuleViolation>(); protected void AddViolation(string message, string property) { AddViolation(message, new[] { property }); } protected void AddViolation(string message, IEnumerable<string> property) { var violation = new RuleViolation(message, property); _violations.Add(violation); } protected abstract void _Validate(string propertyName, object propertyValue, Dictionary<string,object> parameters); public IEnumerable<RuleViolation> Validate(string propertyName, object propertyValue) { return Validate(propertyName, propertyValue, null); } public IEnumerable<RuleViolation> Validate(string propertyName, object propertyValue, Dictionary<string, object> parameters) { _violations.Clear(); _Validate(propertyName, propertyValue, parameters); return new List<RuleViolation>(_violations); } }
Let's get one assumption out the way. This code is not thread-safe. You'll notice I'm using state on the class to handle the validation, by clearing the collection, then calling the derived class, and finally returning the result. If two threads entered this class, there would be an issue.
You might want to add the extra plumbing to make it thread safe, and that's fine by me. I personally am not planning to execute anywhere other than within my view model getters and setters. I'm only validating as the result of user input (except in my testing, of course, where I can control this by creating new instances) which is via databinding. That means that if I code this correctly, any validations will actually only come in one thread: the main UI thread.
Here's what a required value looks like:
public class ValueIsRequired : BaseRule { protected override void _Validate(string propertyName, object propertyValue, Dictionary<string,object> dictionary) { if (propertyValue == null || string.IsNullOrEmpty(propertyValue.ToString())) { AddViolation("Value is required.", propertyName); } } }
Notice that I'm ignoring the dictionary. When checking to see if only alpha characters are in a string (such as a title, for example) I might want to make it optional whether or not to allow spaces in between characters. Here's a rule that takes in an optional parameter:
public class ValueOnlyContainsAlpha : BaseRule { private const string PATTERN = @"[^a-zA-Z]"; private const string PATTERNWITHSPACE = @"[^a-zA-Z ]"; public const string ALLOW_SPACE = "AllowSpace"; protected override void _Validate(string propertyName, object propertyValue, Dictionary<string,object> dictionary) { if (propertyValue is string && Regex.IsMatch(propertyValue.ToString().Trim(), dictionary.GetParameter<bool>(ALLOW_SPACE) ? PATTERNWITHSPACE : PATTERN)) { AddViolation("Value must be alpha only.", propertyName); } } }
(We could extend that a little more and have a separate message to define spaces or not ... this is just here to illustrate the point.) If you're wondering about the GetParameter
method, I extended the dictionary like this:
public static T GetParameter<T>(this Dictionary<string, object> dictionary, string parameterName) { return dictionary == null ? default(T) : dictionary.ContainsKey(parameterName) ? (T) dictionary[parameterName] : default(T); }
This is just a convenience to cast the value or get the default if it is not in the dictionary.
Exporting Rules
The idea with rules is that it should be easy to export them to the system so a centralized location can pass them out. In this case, I can bind validations later in the game but whatever is using the validations must be aware of the type of the rule. To me, that makes perfect sense - a range validation doesn't make sense unless I know there is a range validation rule.
Let's create an export attribute and some metadata for the rule, and we'll just export them by type:
[MetadataAttribute] [AttributeUsage(AttributeTargets.Class,AllowMultiple = false)] public class ExportRuleAttribute : ExportAttribute { public ExportRuleAttribute(Type ruleType) : base(typeof(IValidationRule)) { Rule = ruleType; } public Type Rule { get; set; } } public interface IExportRuleMetadata { Type Rule { get; } }
Now I can export my rules like this:
[ExportRule(typeof(ValueIsRequired))] public class ValueIsRequired : BaseRule
We need to "catch" the rules somewhere. I'll create a signature that mirrors the validation signature, with a factory:
public interface IValidationFactory { IEnumerable<RuleViolation> ValidateThat<T>(string propertyName, object propertyValue) where T : IValidationRule; IEnumerable<RuleViolation> ValidateThat<T>(string propertyName, object propertyValue, Dictionary<string,object> parameters) where T : IValidationRule; }
Here is where the fluent interface starts to creep in. Notice these are not just method names, but names that are starting to make a statement ("validate that ..."). Let's implement this with MEF:
[Export(typeof(IValidationFactory))] public class ValidationFactory : IValidationFactory { [ImportMany(AllowRecomposition = true)] public Lazy<IValidationRule,IExportRuleMetadata>[] Rules { get; set; } private IValidationRule GetRule<T>() where T: IValidationRule { return (from rules in Rules where rules.Metadata.Rule.Equals(typeof (T)) select rules.Value).FirstOrDefault(); } public IEnumerable<RuleViolation> ValidateThat<T>(string propertyName, object propertyValue) where T : IValidationRule { return GetRule<T>().Validate(propertyName, propertyValue); } public IEnumerable<RuleViolation> ValidateThat<T>(string propertyName, object propertyValue, Dictionary<string, object> parameters) where T : IValidationRule { return GetRule<T>().Validate(propertyName, propertyValue, parameters); } }
We capture all of the rules. When the client requests to "validate that..." we'll fetch the type and pass through the parameters.
Fluent Interface
Before I hook this in, I want to add a few extension methods to make the interface more fluent. Remember how I mentioned I'm using a dictionary for additional parameters, but I can type it somewhat? You saw the example in the alpha with the optional spaces. I can send in a parameter that is "true" for allowing spaces. Let's make it easier to pass that parameter, so our interface can look like this:
Validator.ValidateThat<ValueOnlyContainsAlpha>(propertyName,value,ValueOnlyContainsAlpha.ALLOW_SPACE.AsParameterWithValue(true));
That makes it a little easier to read without making a dictionary, adding the values, etc. In fact, we'll also want to extend this (for example, if we need a minimum and maximum range) so I'll also add an extension for additional parameters. The goal is to do this:
Validator.ValidateThat<ValueIsInRange>(propertyName ,value ,ValueIsInRange.MIN.AsParameterWithValue(1) .WithParameter(ValueIsInRange.MAX,10));
Here's what the extension methods look like:
public static Dictionary<string, object> AsParameterWithValue(this string parameterName, object parameterValue) { return new Dictionary<string, object> {{parameterName, parameterValue}}; } public static Dictionary<string, object> WithParameter(this Dictionary<string, object> dictionary, string parameterName, object parameterValue) { dictionary.Add(parameterName, parameterValue); return dictionary; }
The last extension I want to create for our fluent interface is to allow chaining of validations. In most situations, you'll have more than one validation. Because validations return an enumeration of violations, it is easy enough to combine them by merging their results. Here's what the goal is:
var violations = Validator.ValidateThat<ValueIsRequired>(propertyName,value) .AndAlso(()=>Validator.ValidateThat<ValueOnlyContainsAlpha>(propertyName ,value ,ValueOnlyContainsAlpha.ALLOW_SPACE.AsParameterWithValue(true)));
We accomplish the merge like this:
public static IEnumerable<RuleViolation> AndAlso(this IEnumerable<RuleViolation> source, Func<IEnumerable<RuleViolation>> target) { var list = new List<RuleViolation>(source); list.AddRange(target()); return list; }
Using PRISM to Host the Validations
Now the validation project can easily be shared/linked to .NET and used for server-side validation. We're more concerned with the immediate UI feedback. How does this engine integrate with Model-View-ViewModel?
PRISM provides MVVM guidance in their preview 4. Download it here. There is a base MVVM class in the MVVM quickstart that implements helpers for notify property changed events as well as IDataErrorInfo
, a standard means for identifying validation errors. John Papa discusses this interface here.
What we want to do is hook into the supplied class and make it easy to validate. The reference implementation already has methods for setting and clearing errors against properties, so we just want to provide a validation "front-end" to hook into this feature.
First, we'll expose our validation factory by importing it into the base view model class:
[Import] public IValidationFactory Validator { get; set; }
Next, we'll supply a method that derived view models can use to hook into validations:
protected virtual void WithValidationFor<T>(Expression<Func<T>> propertyExpression, IEnumerable<RuleViolation> violations) { var propertyName = ExtractPropertyName(propertyExpression); var hasViolations = false; foreach(var violation in violations) { hasViolations = true; SetError(propertyName, violation.Message); } if (!hasViolations) { ClearErrors(propertyName); } }
Very simple - we simply parse the validations, and if any exist, we set them on the property, otherwise we reset them.
Putting it All Together
I'm not supplying full source on purpose - obviously not everyone will use the PRISM reference implementation for a solution and may have another framework they'd prefer to use, and the intent here is educational. In this example, I can validate that a name is required and must be alpha only (with spaces) like this on a view model derived from the base:
private string _name; public string Name { get { return _name; } set { _name = value; RaisePropertyChanged(() => Name); var propertyName = ExtractPropertyName(() => Name); WithValidationFor(()=>Name, Validator.ValidateThat<ValueIsRequired>(propertyName,value) .AndAlso( ()=>Validator.ValidateThat<ValueOnlyContainsAlpha>(propertyName,value,ValueOnlyContainsAlpha.ALLOW_SPACE.AsParameterWithValue(true)))); } }
And there you have it: a (somewhat) fluent interface for validation that sits on top of PRISM's MVVM quickstart guidance.