Monday, April 27, 2009

An Asp.Net Validation Framework (Part 3)

An Asp.Net Validation Framework (Part 1)

An Asp.Net Validation Framework (Part 2)

Unfortunately, if you’re using LINQ to SQL or the Entity Framework, you can’t add the custom attributes we need for validation. You could generate your entity model and then simply use those generated objects but you would lose the ability to use the designers. I prefer to leverage the code we’ve written.

public class ValidatableOrmBase
: ValidatableBase
{

private List<CustomPropertyAttributeListItem> customPropertyAttributes;

public List<CustomPropertyAttributeListItem> CustomPropertyAttributes
{
get
{
if (customPropertyAttributes == null)
{
customPropertyAttributes = new List<CustomPropertyAttributeListItem>();
}
return customPropertyAttributes;
}
}

public override bool Validate(bool clearBrokenRules)
{
base.Validate(clearBrokenRules);
foreach (CustomPropertyAttributeListItem cvli in CustomPropertyAttributes)
{
PropertyInfo theProperty = this.GetType().GetProperty(cvli.PropertyName, BindingFlags.Public | BindingFlags.Instance);
if (!cvli.CustomAttribute.IsValid(theProperty.GetValue(this, null), cvli.PropertyName))
{
BrokenRules.Add(cvli.CustomAttribute.Rule);
}
}

return BrokenRules.Count == 0;
}
}
public class CustomPropertyAttributeListItem
{
public string PropertyName { get; set; }

public ValidationAttribute CustomAttribute { get; set; }
}

All we’ve done here is create a subclass of ValidatableBase that has a CustomPropertyAttributes property of type List<CustomPropertyAttributeListItem>. Basically, we’re building a list of attributes and the the property to which they need to be applied. Validate is then overridden to call the base (just in case there are attributes) and the loop through the list.

This technique uses exactly the same code for validation, our public interface stays the same (in case they change things so we can add attributes) and we haven’t sacrificed anything from an extensibility perspective.

partial class MyLinqToSqlClass    
: ValidatableOrmBase
{
public MyLinqToSqlClass()
: base()
{
this.CustomPropertyAttributes.Add(
new CustomPropertyAttributeListItem() {
PropertyName = "FirstName",
CustomAttribute = new RequiredAttribute() {
DisplayName = "first name" } }); }
}

Now to implement, we simply create our partial class and inherit from ValidatableOrmBase. Ok, we’re all done. Wait! You can’t use the partial class on EF entities since they already inherit from another class. Rats!!!!! Don’t fret, there is a way to accomplish our goals.

First, let’s create a new base class that implements IValidatable.

public class ValidatableEfBase
: IValidatable
{

/// <summary>
/// Holds the collection of broken business rules.
/// </summary>
private BrokenRulesCollection brokenRules;

private List<CustomPropertyAttributeListItem> customPropertyAttributes;

/// <summary>
/// Gets the collection of business rules that have
/// been broken.
/// </summary>
public BrokenRulesCollection BrokenRules
{
get
{
if (this.brokenRules == null)
{
this.brokenRules = new BrokenRulesCollection();
}
return this.brokenRules;
}
}

public List<CustomPropertyAttributeListItem> CustomPropertyAttributes
{
get
{
if (customPropertyAttributes == null)
{
customPropertyAttributes = new List<CustomPropertyAttributeListItem>();
}
return customPropertyAttributes;
}
}

public EntityObject Entity { get; set; }

/// <summary>
/// Validates the objects.
/// </summary>
/// <returns>True if the object is valid, otherwise false.</returns>
public bool Validate(bool clearBrokenRules)
{
if (clearBrokenRules)
{
this.BrokenRules.Clear();
}

foreach (CustomPropertyAttributeListItem cvli in CustomPropertyAttributes)
{
PropertyInfo theProperty = Entity.GetType().GetProperty(cvli.PropertyName, BindingFlags.Public | BindingFlags.Instance);
if (!cvli.CustomAttribute.IsValid(theProperty.GetValue(Entity, null), cvli.PropertyName))
{
BrokenRules.Add(cvli.CustomAttribute.Rule);
}
}

return BrokenRules.Count == 0;
}
}

Ok, we have all our functionality and it’s centrally located. Now, how do we expose it. Simple, we use partial classes and defer the validation to an instance of the above. Like so.

public partial class MyEFEntity
: IValidatable
{
public BrokenRulesCollection BrokenRules
{
get
{
return validator.BrokenRules;
}
}

private ValidatableEfBase validator;

protected ValidatableEfBase Validator
{
get
{
if (validator == null)
{
validator = new ValidatableEfBase();
validator.Entity = this;
}
return validator;
}
}

public bool Validate(bool clearBrokenRules)
{
CreateCustomAttributes(false);
return Validator.Validate(clearBrokenRules);
}

protected void CreateCustomAttributes(bool clearList)
{
if (clearList)
{
Validator.CustomPropertyAttributes.Clear();
}
Validator.CustomPropertyAttributes.Add(
new CustomPropertyAttributeListItem() { PropertyName = "Name", CustomAttribute = new RequiredAttribute() { DisplayName = "name" } });
}
}

Notice we have BrokenRules and Validate so we’ve met our interface requirements, meaning it looks the same from the outside. This is important for both techniques since we want to be able to change the internal implementation if MS changes things so we can add our attributes.

What happens underneath is simple. The Validator (an instance of ValidatableEfBase) actually performs all the validation on the EF entity.

That wraps up the Validation Framework. Next, I’ll look at how to create a client side validation framework for use with Asp.Net MVC.

No comments: