You can only discuss functionality from the aspect of the impact the functionality has on a given consumer. Given the fact that we are discussing a “framework”, the consumer that is going to be most impacted is the developer consuming the framework. After all, a developer can deliver the same user experience with or without the use of the framework. A good framework should allow the developer to deliver this user experience with the least amount of effort while increasing the maintainability. So with that in mind lets discuss some functionality I would expect a Validation Framework to deliver.
Dictionary.com defines fluent as “ able to speak or write smoothly, easily, or readily”. Eric Evans and Martin Fowler first coined the term “fluent interface” as it applies to an API, allowing the developer to easily and readily interact with it. This is usually done through the use of method chaining. By implementing a fluent interface, a Validation Framework will all a developer to easily express validation rules, leveraging intellisense along the way.
Check(c => c.FirstName).Required().MaxLength(50).IsAlpha();
Use of Expressions
Many frameworks rely on strings being passed in as parameters that indicate a property that is to be referenced or a method that is to be invoked. The problem with doing so is that these strings often get missed when renaming a property or method. By using expressions, represented as lambdas, we not only facilitate all the renaming, but also give the developer more flexibility to express what they wish in the lambda, and allow them to take advantage of intellisense – something lacking when defining a string.
Of course, a Validation Framework should allow me to utilize expressions when declaring the target of a rule such as when I want to declare a rule for the StartDate property of a class defining a Meeting:
Check(m => m.StartDate).Required();
However, in a lot of situations, a rule needs to reference other properties, or call other methods, or rely on some other piece of data that is declared elsewhere in application. This is where I see a lot of Validation Frameworks fall short. Consider a class defining a Meeting where a rule may be “StartDate must be before EndDate”. Many frameworks require you to implement your own delegate and call it as part of a “custom” rule. SpecExpress allows you to declare this rule as part of the standard GreaterThan or LessThan rules:
Check(m => m.StartDate).Required().LessThan(m => m.EndDate);
A matter of fact all argument based rules that are implemented by SpecExpress have an overload that takes a constant, as well as an overload taking in an expression.
Implementation of Most Common Rules
A Validation Framework is only as useful as the rules it implements out of the box. In my opinion, this is another area where a lot of frameworks lack. Many frameworks give you a subset and leave the rest to be implemented by you – the consuming developer.
At last count, SpecExpress has implemented 30 rules that cover:
- Boolean (IsTrue, IsFalse)
- Collections (Contains, IsEmpty, etc.)
- DateTime (IsInFuture, IsInPast)
- IComparable (Between, IsGreaterThan, IsLessThan, etc.)
- String (MaxLength, MinLength, Alpha, Numeric, Matches, etc.)
- Custom (for the rare occasion where you need it)
Often times, there are relations between rules. For instance, I may have rules for a serial number that states “The serial number must start with a ‘y’ or a ‘z’ and be 10 characters long”. Many frameworks consider this to be a “complex rule” that must be implemented in a custom delegate, but in SpecExpress, by using the Matches and LengthEquals rules, this can be expressed through the fluent interface and not relegated to be implemented in a delegate:
Check(i => i.SerialNumber).Required()
.Group(s => s.Matches("^y.*")
In the above example, a Group is defined to control the precedence of the “And” and the “Or” operations. Just like in normal C#, an “And” takes precedence over the an “Or”.
By focusing on the developer experience, SpecExpress has provided a Validation Framework not only assists the developer express the rules through Intellisense, but also reduces the amount of code that code they would need to write for what other frameworks would consider “complex” validation. However, SpecExpress has not ignored the fact that there may be situations that were either simply overlooked, or would be better expressed through extending SpecExpress. In my next post, I will show two ways to extend and customize SpecExpress.