A common requirement when developing applications is the ability to validate domain objects and display the subsequent validation errors on the user interface.
One method of implementing this is through the use of the IDataErrorInfo interface. This interface expects the class to implement two properties, Error and Item
Shown below is a class with 2 properties upon which I would like to perform validation
ClassE = public class (IDataErrorInfo)
{ Methods }
public constructor;
{ Properties }
public property AnotherStringProperty: String;
public property StringProperty: String;
public property Error: String;
public property Item[columnName: String]: String read get_Item;
end;
Shown below is the implementation of the get_item method
method ClassE.get_Item(columnName: String): String;
begin
var str: String;
if ((String.Compare('AnotherStringProperty', columnName) = 0) and (not String.IsNullOrEmpty(self.AnotherStringProperty) and iif((self.AnotherStringProperty.Length > 5), (self.AnotherStringProperty.Length >= 10), true))) then
str := 'AnotherStringProperty must fall between 5 and 10';
if ((String.Compare('StringProperty', columnName) = 0) and (not String.IsNullOrEmpty(self.StringProperty) and (self.StringProperty.Length >= 10))) then
str := 'StringProperty must be less than 10';
begin
result := str;
exit
end
end;
In this method I have specific validation
1) AnotherStringProperty must be between 5 and 10 characters
2) StringProperty must be longer than 9 characters.
It gets pretty tedious having to implement this and there are usually alot of common validation requirements that can be shared across domain objects. For example
1) String lengths
2) Numbers falling between specific ranges
In this blog post I will be presenting a solution to this problem using the AOP framework called Cirrus in Delphi Prism.
Consider an Employee object as shown below
Employee=public class
private
method ValidateBirthDate:Boolean;
public
property Id:Integer;
property Firstname:String;
property Lastname:String;
property BirthDate:DateTime;
property Salary:Decimal;
property SomeNumber:Int32;
method IsSalaryValid():Boolean;
end;
It has some basic validation requirements
1) An Id is required
2) The firstname and lastnames must be set
3) The date of birth must be valid
4) The BirthDate must be valid. I treated this as "complex", so the class has a method to validate this
Shown below is how I decorate the class with aspects using my validation framework to meet the validation requirements listed above.
[aspect:Prism.StandardAspects.Validation.Aspects.Validation]
Employee=public class
private
method ValidateBirthDate:Boolean;
public
[aspect:Prism.StandardAspects.Validation.Aspects.NumericHasValue('An Id is Required')]
property Id:Integer;
[aspect:Prism.StandardAspects.Validation.Aspects.StringNullOrEmpty()]
property Firstname:String;
[aspect:Prism.StandardAspects.Validation.Aspects.StringNullOrEmpty('You must fill in the employees lastname')]
property Lastname:String;
[aspect:Prism.StandardAspects.Validation.Aspects.ValidationMethodForProperty('ValidateBirthDate','The date of birth must be valid')]
property BirthDate:DateTime;
property Salary:Decimal;
[aspect:Prism.StandardAspects.Validation.Aspects.ValidationMethod('We are not paying this employee')]
method IsSalaryValid():Boolean;
end;
Each validation aspect takes parameters to display messages in the user interface if the property fails validation. If there are requirements not met by the supplied aspects you can implement your own methods.
In the code above the method validateBirthDate is used to validate the BirthDate property.
At compile time the IDataErrorInfo interface is implemented on the domain object. An IsValid method is also exposed and can be used in non user interface code.
The IDataErrorInfo interface is suppported in WPF, Silverlight 4 and ASP.Net MVC. Domain objects can be bound to the user interface elements, validation can be supported
and validation errors can be displayed to the user while they are editing. Shown below are screen shots of when validation has failed in each environment.
The source code for the aspects and examples can be found at the RemObjects code repository
This is an open source project that contains a number of aspects developed using the Cirrus AOP framework for Delphi Prism.
Show below is the Solution open in Visual Studio 2010. A word of warning you will need Delphi Prism 2011 to build the solution
I would like to highlight several projects in the Solution. Its split between implementation and examples.
Prism.StandardAspects.Validation
|
base framework added to projects consuming the validation aspects
|
Prism.StandardAspects.Validation.Silverlight |
As above but for Silverlight
|
Prism.StandardAspects.ValidationAspects |
The Aspects
|
|
|
ValidationDomain
|
A domain object using validation
|
ValidationDomain.Tests |
NUnit tests for the framework |
IDataErrorInfoWindowsApplication
|
A WPF application showing the domain object bound to the UI |
ValidationMVCApplication
|
An ASP.Net MVC application showing editing and displaying of domain objects |
ValidationSilverlightApplication
|
A Silverlight 4 application, with the domain object bound to the UI |
|
|
A combination of the NUnit tests and examples should give a good starting point.
Feedback is welcome either through the comments or email.
Cheers
John