Tuesday, 4 November 2008

Validating Entity Framework Objects Using Reflection and Generics and the Validation Application Block

I’ve spent much of today trying to minimise the amount of work I’m going to be required to do during my project and this is what I’ve come up with.  We are working with the Entity Framework and Linq to Entities, however we also want to implement some custom validation of fields where instead of writing validation rules.

We found this to be a little difficult.  One thing about the validation application block, it does not support inheritance.  If you have a number of objects that inherit from a base object you may want to define a type for the  base object so that it will pick up all seven types in validation time and validate them against the base type.  But it doesn’t work.  For example in the code below.

BaseClass objectToValidate = new SuperClass();
Validation.Validate(objectToValidate)

This code will not validate against the type BaseClass (assuming BaseClass is the base type of SuperClass).  You can modify your code so that you tell it which class to validate against like so:

SuperClass objectToValidate = new SuperClass();
Validation.Validate<BaseClass>(objectToValidate)

But this is quite difficult if you’re trying to remain generic.  For example in our application we are wrapping up the Validation application block in an Object Method design pattern to hide the implementation from the user and allow simple unit testing.

In our case, this method will not work because it thinks that the object type is System.Object.

public ValidationResults Validate(object toValidate)
{
    ValidationResults msResults = Validation.Validate(toValidate);
    return returnList;
}

I wouldn't recommend that you use the validation application block directly as it will make unit testing quite difficult.  To unit test effectively you will need to mock out an interface into validation for the rest of your code where the testing of validation is not relevant.  You can work around this with Generics.

Using Generics implementation, the method above turns into this.

public ValidationResults Validate<T>(T toValidate)
{
    ValidationResults msResults = Validation.Validate(toValidate);
    return returnList;
}

This will work and your object will remain of its own type.  It also allows you to validate against other object types (like base classes) by defining the object type in the method call using angle brackets.

How does this relate to the entity framework?  Well all objects in the entity framework inherit from a singular type of System.Data.Objects.DataClasses.EntityObject.  I’ve covered before how you can create your own custom validators with the Validation Application Block.  If we are able to create a custom database validator, catch the EntityObject and then investigate the object to discern its members from the entity framework we will be able to validate it against its database types.

I think it’s easier to explain by example, so here is my DoValidate method of my custom validator.

protected override void DoValidate(object objectToValidate, object currentTarget, string key, 
    ValidationResults validationResults)
{
    // Get the type of the object.
    string typeName = objectToValidate.GetType().Name;
 
    // Get the parameters
    PropertyInfo[] propertyInfos = objectToValidate.GetType().GetProperties();
 
    foreach (PropertyInfo propertyInfo in propertyInfos)
    {
        // Ignore entity state and entity key, they're variables of the entity framework.
        if ((!propertyInfo.Name.Equals("EntityState") && !propertyInfo.Name.Equals("EntityKey")))
        {
            if (propertyInfo.PropertyType.Equals(typeof(string)))
            {
                ValidateString(objectToValidate, typeName, propertyInfo.Name, 
                    (string)propertyInfo.GetValue(objectToValidate, null), validationResults);
            }
        }
    }
 
 
    rfSysObjectFieldDetail field = _FieldDetailRepository.GetField("MockEntityObject", "ID");
}

.NET Reflection is used (System.Reflection) to look into the properties of the object I’m validating.  We ignore EntityState and EntityKey as they’re base types and do not reference the database.  If you’re pulled your Entity Model straight from the database then the rest of the types will directly reference the database fields with the class name referencing the table.  The method above only validates strings so far.

The method ValidateString just looks into the database for the field definition and verifies that the string is within the correct length parameters.

private void ValidateString(object objectToValidate, string tableName, string fieldName, 
    string fieldValue, ValidationResults validationResults)
{
    rfSysObjectFieldDetail field = _FieldDetailRepository.GetField(tableName, fieldName);
 
    int fieldLength = fieldValue.Length;
 
    // Check the field length.
    if (fieldValue.Length > field.MaximumLength)
    {
        // TODO These must be loaded from the messages object
        ValidationResult result = new ValidationResult(
            "String length too long, maximum length is " + field.MaximumLength,
            objectToValidate, "", "", this);
 
        validationResults.AddResult(result);
    }
 
    if (fieldValue.Length < field.MinimumLength)
    {
        // TODO This must be loaded from the messages objects
        ValidationResult result = new ValidationResult(
            "String length too short, minimum length is " + field.MinimumLength,
            objectToValidate, "", "", this);
 
        validationResults.AddResult(result);
    }
 
}

And that’s about it.  Hopefully it’s useful to someone other than just myself.

3 comments:

Ariel said...

Is there some working example that we can see of this? I'm very interested and trying to implement generic validation methods with EF.
Thanks!

Ariel

Steven said...

I'm sorry Dan, but your statement about inheritance is incorrect. Validation Application Block can easily do what you which, just don't use the Validator.Validate<T>(T) method. You should use the ValidationFactory.CreateValidator(Type) method to create a validator for a specific type. Look at the following code:

BaseClass entity = new SuperClass();

var validator = ValidationFactory.CreateValidator(entity.GetType());
validator.Validate(entity);

I wrote a couple articles about integrating VAB with O/RM mappers such as Entity Framework. Perhaps they might be of some use to you.

Cheers.

Odd said...

Hey Steve,

Good to know, I suppose I didn't go deep enough with my inquiry into the frame-work. Nice articles and site.

Cheers
Daniel