Click here to Skip to main content
Click here to Skip to main content

Creating Validation Engine for Domain Objects

By , 15 Aug 2008
 

Introduction

The brain of the application is represented by the domain. The domain consists of the business rules which make the application function properly. The rules must be validated in order to guarantee a successful operation. In this article we will build a simple domain object validation framework using custom attributes and reflection.

What does Domain Object Validation Means?

First we need to understand what domain objects validation means. Let’s consider a simple class Customer which is domain object. The Customer class consists of properties like FirstName, LastName etc. We need to make sure that the user does not leave FirstName and LastName empty. Of course, we can always use ASP.NET validation controls to make sure that the user inserts the fields but that validation is performed on the user interface level. We need to provide another layer of validation which validate the object based on the more complex business rules.

The Class Diagram

Take a look at the complete class diagram of the validation framework.

classdiagramvalidationframework_small.PNG

Now, let’s take a dive into the implementation.

Creating the Abstract ValidationAttribute Custom Attribute

The first task is to create custom attributes. To create an attribute your class must inherit from System.Attribute. Check out the implementation of the ValidationAttribute class below which serves as the abstract base class for all the validation attributes.

   public abstract class ValidationAttribute : System.Attribute
    {
        public string Message { get; set; }

        public abstract bool IsValid(object item);        
    }

The ValidationAttribute class contains the properties and the methods that will be used and implemented by all the other validation classes.

Implementing the NotNullOrEmptyAttribute

Let’s implement the first validation attribute “NotNullOrEmptyAttribute.” This will make sure that the value of an object is not null or empty.

[AttributeUsage(AttributeTargets.Property)]
    public class NotNullOrEmptyAttribute : ValidationAttribute
    {       

        public NotNullOrEmptyAttribute(string message)
        {
            Message = message;
        }
        
        public override bool IsValid(object item)
        {
            if (String.IsNullOrEmpty((string)item))
                return false;

            return true;             
        }
    }

As, you can see the implementation is quite simple! The IsValid method checks that if the value passed is valid or not. The good thing about using attribute based validation is that you can add more rules just by adding more custom attributes. The attribute can be decorated on the Customer class using the following syntax.

public class Customer : BusinessBase
    {
        [NotNullOrEmpty("First name cannot be null or empty")]
        public string FirstName { get; set; }

        [NotNullOrEmpty("First name cannot be null or empty")]
        public string LastName { get; set; }
    }

Implementing the ValidationEngine

The ValidationEngine class is responsible for validating the business objects. Here is the complete implementation of the ValidationEngine.Validate<T> method.

public static bool Validate<T>(T item) where T : BusinessBase
        {
            var properties = item.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.Instance);

            foreach (var property in properties)
            {
                var customAtt = property.GetCustomAttributes(typeof(ValidationAttribute),
                    true);

                foreach (var att in customAtt)
                {
                    var valAtt = att as ValidationAttribute;
                    if (valAtt == null) continue;

                    if (valAtt.IsValid(property.GetValue(item, null))) continue;

                    var brokenRule = new BrokenRule
                                         {
                                             Message = String.Format("{0}:{1}",
                                             property.Name, valAtt.Message),
                                             PropertyName = property.Name
                                         };
                    item.BrokenRules.Add(brokenRule);
                }

            }

            return (item.BrokenRules.Count == 0);
        }

The Validate<T> method simply extracts all the properties from the object and check to see if it is decorated with the custom attribute of ValidationAttribute type. If it is then it invokes the IsValid method on the custom attribute class. If the object is not valid then the broken rules are added to the BrokenRules collection.

If you are more interested in validating your objects using the Customer.IsValid syntax then check out the following article which uses extension methods to add the extended functionality.

Desinging Application Using Test Driven Development Part 2

Using the ValidationEngine

ValidationEngine is pretty simple to use. Simply, pass in your object to the ValidationEngine.Validate<T> method and check to see if your object is valid or not based on the returned value. You also need to make sure that your object’s properties are decorated with correct validation attributes.

static void Main(string[] args)
        {
            var customer = new Customer();

            ValidationEngine.Validate(customer);

            foreach(var brokenRule in customer.BrokenRules)
            {
                Console.WriteLine(brokenRule.Message);
            }
        }

Also, note that BrokenRules is a List<BrokenRule> collection which implements the IEnumerable interface. This means that you can easily bind your broken rules to a databound control like Repeater, ListView, DataList, DataGrid or GridView.

validationEngine_002.GIF

Conclusion

In this article we learned how to validate the business objects using custom attributes and reflection. You can extend the ValidationEngine class by providing more custom attributes each targeting a certain area of validation.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

azamsharp
Web Developer
United States United States
Member
I am the founder of knowledge base website, HighOnCoding, GridViewGuy, RefactorCode.com and ScreencastADay.com.
 
HighOnCoding is a website which will get you high legally with useful information. There are tons of articles, videos and podcasts hosted on HighOnCoding.
 
HighOnCoding.com www.HighOnCoding.com
 

My Blog:

Blog

 

Buy my iPhone app ABC Pop

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralUse CasememberChris Mulvey27 Apr '10 - 12:25 
I have used this domain model validation in a number of real world applications very successfully. These were non-trivial applications in both the private and public sectors.
 
You can validate multiple property values to infer business constraints within the object. For example, we can validate that property X must be within a certain range if, and only if, property Y is true|false, negative|positive, etc. We have defined these as constrained validators and they are simple to implement. When the reflection engine has access to the desired property value, you then reflect once more over the object to find the constraining property value and validate as required...simples!
 
Further, using this technique, we have implemented a set of validators such that validation against the property will only take place if a second property meets a given dependency constraint. Also, we have implement optional validation ina similar manner such that, of the specified attributes, only those flagged as Optional will be executed if a second property meets a given dependency constraint.
 
Great Work, concise yet powerful.
McNulty

GeneralRe: Use Casememberazamsharp27 Apr '10 - 14:19 
Hi,
 

Wow! I am so happy that you are able to use it in real world application. If you can do share your different validators.
 
Thanks,
Azam
Mohammad Azam
azamsharp@gmail.com
www.highoncoding.com
Houston, TEXAS

GeneralGreat examplememberhammerstein0520 Nov '09 - 7:18 
Found the code really easy to follow and understand. Thank-you, you've given me some great ideas.
Generalsome thoughtsmemberDaProgramma18 Aug '08 - 21:18 
Hello,
your work is a simple example for iterating attributes, and it is OK so far.
But its no good idea to put validation logic into attributes. Some problems:
 
- cannot work with autogenerated code (e.g. with Entity Framework or any other ORM mapper)
 
- limited to single entity. How would you express for example, that if Name is given, Firstname must be given, too, but it would be OK to have both empty? You need a validation rule that covers Name and Firstname together.
 
Some more subtle things:
 
- cannot be used with standard databinding
 
- From a maintenance view, it would be much easier to have a static class, lets say "PersonValidater" with static methods that do the validations. Standard-Validation logic (like "cannot be empty") could be factored out to reusable helper functions.
 
Greetz
daProgramma
GeneralValidation Engine and Attributesmemberliammclennan18 Aug '08 - 15:37 
Great work Azam! I have been looking for something just like this. I agree with your comment above about the enterprise library validation framework. I have been using it but I don't like. It is complex and brings huge overhead when all I want is validation. However, there is one thing stopping me from using your library. My business objects and their properties are code-generated (by the linq-to-sql designer) so I don't want to add attributes to them. Is there any other way to associate validators to properties?
 
Liam McLennan
liam@eclipsewebsolutions.com.au
www.eclipsewebsolutions.com.au

GeneralRe: Validation Engine and Attributesmemberazamsharp18 Aug '08 - 16:09 
Excellent question!
 
Yes you cannot put attributes on the auto-generated code since it will be overriden the next time the code is generated. You can however place the validation settings in the XML file and then load those settings for the domain object.
 
Here is such an XML file:
 

 


Message ="FirstName cannot be null or empty" />
 

Message ="LastName cannot be null or empty" />

 

 
And here are the new methods for the ValidationEngine framework:
 
public static ValidationSettings GetValidationSettingFor()
{
ValidationSettings settings = new ValidationSettings();
 
XmlDocument doc = new XmlDocument();
string path = ConfigurationManager.AppSettings["ValidationSettingsPath"] as String;

// each time the xml file is loaded into memory this is kinda sucky!
doc.Load(path);
 
Type type = typeof(T);
 
XmlNode node = doc.SelectSingleNode("ValidationSettings/"+ type.FullName);
 
if (node == null) throw new ArgumentNullException(String.Format("Validation for {0} is not defined",type.FullName));
 
XmlNodeList propertyNodeList = node.SelectNodes("Property");
 
// fill the ValidationSetting entity!
 
foreach (XmlNode propertyNode in propertyNodeList)
{
ValidationSetting setting = new ValidationSetting();
setting.PropertyName = propertyNode.Attributes["Name"].Value;
setting.Message = propertyNode.Attributes["Message"].Value;
 
setting.ValidationType = (ValidationAttribute) Activator.CreateInstance(Type.GetType(propertyNode.Attributes["ValidationType"].Value));

 
settings.Add(setting);
}
 

return settings;
}
 
public static bool Validate2(T item) where T : BusinessBase
{
ValidationSettings settings = GetValidationSettingFor();
 
PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
 
foreach (PropertyInfo property in properties)
{
ValidationSetting vs = settings[property.Name];
 
if (vs == null) break;

ValidationAttribute valAtt = vs.ValidationType as ValidationAttribute;
 
if (valAtt != null)
{
if (!valAtt.IsValid(property.GetValue(item, null)))
{
BrokenRule brokenRule = new BrokenRule();
brokenRule.Message = String.Format("{0}:{1}", vs.PropertyName, vs.Message);
brokenRule.PropertyName = property.Name;
item.BrokenRules.Add(brokenRule);
}
}

}

return false;
}
 

Here is the ValidationSettings class:
 
public class ValidationSettings : List
{
public ValidationSetting this[string name]
{
 
get
{
return this.Find(delegate(ValidationSetting vs) {
return vs.PropertyName == name;
});
 

}
}
 
}
 

And here is the ValidationSetting class:
 
public class ValidationSetting
{
private string _propertyName;
private string _message;
private ValidationAttribute _validationType;

public string PropertyName
{
get { return _propertyName; }
set { _propertyName = value; }
}
 
public string Message
{
get { return _message; }
set { _message = value; }
}
 
public ValidationAttribute ValidationType
{
get { return _validationType; }
set { _validationType = value; }
}
 
}
 
Hope it helps!
Azam
 
Mohammad Azam
azamsharp@gmail.com
www.gridviewguy.com
Houston, TEXAS

GeneralRe: Validation Engine and AttributesmemberManishaPol18 Mar '10 - 2:00 
Thank you for infomation given. I want to check the XML file used validation. Please provide me the sample XML file.
GeneralCSLAmemberHoyaSaxa9316 Aug '08 - 3:40 
Have you seen Rockford Lothka's CSLA framework?
 
There are many similarities in this article to classes in his Validation namespace (Source)
GeneralRe: CSLAmemberazamsharp16 Aug '08 - 4:19 
You can use CSLA framework but this article is about creating a validation framework. I think if I just want to use validation then using CSLA will be complicated since it is a huge framework.
 
Mohammad Azam
azamsharp@gmail.com
www.gridviewguy.com
Houston, TEXAS

QuestionWhy don't you use the Validation Application Block?memberKing_kLAx15 Aug '08 - 22:41 
I have used this on multiple projects and have no complaints:
 
http://msdn.microsoft.com/en-us/library/cc309320.aspx[^]
 
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
public class Customer
{
    [StringLengthValidator(0, 20)]
    public string CustomerName;
 
    public Customer(string customerName)
    {
        this.CustomerName = customerName;
    }
}
 
public class MyExample
{
    public static void Main() 
    {
        Customer myCustomer = new Customer("A name that is too long");
        ValidationResults r = Validation.Validate<Customer>(myCustomer);
        if (!r.IsValid)
        {
            throw new InvalidOperationException("Validation error found.");
        }
    }
} 

 
Ian

AnswerRe: Why don't you use the Validation Application Block?memberazamsharp16 Aug '08 - 4:17 
You can use the Validation Application Block but I tend to stay away from the Enterprise Library. For one thing the .NET framework keep on changing and this makes the Enterprise Library obsolete. I had a very hard time porting the Enterprise Library from version 1 to version 3.
 
Also, it is very hard to debug the Enterprise Library and sometimes when you are stuck you don't know if this is a problem in your code or the Enterprise Library.
 
Mohammad Azam
azamsharp@gmail.com
www.gridviewguy.com
Houston, TEXAS

GeneralGood onememberN a v a n e e t h15 Aug '08 - 17:58 
Hi,
 
This is a good approach. Deserve a 5. CP has one more fantastic article related to this subject. Please take a look at http://www.codeproject.com/KB/cs/DelegateBusinessObjects.aspx[^]. It's worth reading.
 
All C# applications should call Application.Quit(); in the beginning to avoid any .NET problems.- Unclyclopedia

How to use google | Ask smart questions

GeneralRe: Good onememberazamsharp16 Aug '08 - 4:12 
I am glad you liked the article.
 
Mohammad Azam
azamsharp@gmail.com
www.gridviewguy.com
Houston, TEXAS

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 15 Aug 2008
Article Copyright 2008 by azamsharp
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid