Los Techies : Blogs about software and anything tech!

Entity validation with visitors and extension methods


On the Yahoo ALT.NET group, an interesting conversation sprung up around the topic of validation.  Entity validation can be a tricky beast, as validation rules typically depend on the context of the operation (persistence, business rules, etc.).

In complex scenarios, validation usually winds up using the Visitor pattern, but that pattern can be slightly convoluted to use from client code.  With extension methods in C# 3.0, the Visitor pattern can be made a little easier.

Some simple validation

In our fictional e-commerce application, we have a simple Order object.  Right now, all it contains are an identifier and the customer's name that placed the order:

public class Order
{
    public int Id { get; set; }
    public string Customer { get; set; }
}

Nothing too fancy, but now the business owner comes along and requests some validation rules.  Orders need to have an ID and a customer to be valid for persistence.  That's not too hard, I can just add a couple of methods to the Order class to accomplish this.

The other requirement is to have a list of broken rules in case the object isn't valid, so the end user can fix any issues.  Here's what we came up with:

public class Order
{
    public int Id { get; set; }
    public string Customer { get; set; }

    public bool IsValid()
    {
        return BrokenRules().Count() > 0;
    }

    public IEnumerable<string> BrokenRules()
    {
        if (Id < 0)
            yield return "Id cannot be less than 0.";

        if (string.IsNullOrEmpty(Customer))
            yield return "Must include a customer.";

        yield break;
    }
}

Still fairly simple, though I'm starting to bleed other concerns into my entity class, such as persistence validation.  I'd rather not have persistence concerns mixed in with my domain model, it should be another concern altogether.

Using validators

Right now I have one context for validation, but what happens when the business owner requests display validation?  In addition to that, my business owner now has a black list of customers she won't sell to, so now I need to have a black list validation, but that's really separate from display or persistence validation.  I don't want to keep adding these different validation rules to Order, as some rules are only valid in certain contexts.

One common solution is to use a validation class together with the Visitor pattern to validate arbitrary business/infrastructure rules.  First, I'll need to define a generic validation interface, as I have lots of entity classes that need validation (Order, Quote, Cart, etc.):

public interface IValidator<T>
{
    bool IsValid(T entity);
    IEnumerable<string> BrokenRules(T entity);
}

Some example validators might be "OrderPersistenceValidator : IValidator<Order>", or "CustomerBlacklistValidator : IValidator<Customer>", etc.  With this interface in place, I modify the Order class to use the Visitor pattern.  The Visitor will be the Validator, and the Visitee will be the entity class:

public interface IValidatable<T>
{
    bool Validate(IValidator<T> validator, out IEnumerable<string> brokenRules);
}

public class Order : IValidatable<Order>
{
    public int Id { get; set; }
    public string Customer { get; set; }

    public bool Validate(IValidator<Order> validator, out IEnumerable<string> brokenRules)
    {
        brokenRules = validator.BrokenRules(this);
        return validator.IsValid(this);
    }
}

I also created the "IValidatable" interface so I can keep track of what can be validated and what can't.  The original validation logic that was in Order is now pulled out to a separate class:

public class OrderPersistenceValidator : IValidator<Order>
{
    public bool IsValid(Order entity)
    {
        return BrokenRules(entity).Count() > 0;
    }

    public IEnumerable<string> BrokenRules(Order entity)
    {
        if (entity.Id < 0)
            yield return "Id cannot be less than 0.";

        if (string.IsNullOrEmpty(entity.Customer))
            yield return "Must include a customer.";

        yield break;
    }
}

This class can now be in a completely different namespace or assembly, and now my validation logic is completely separate from my entities.

Extension method mixins

Client code is a little ugly with the Visitor pattern:

Order order = new Order();
OrderPersistenceValidator validator = new OrderPersistenceValidator();

IEnumerable<string> brokenRules;
bool isValid = order.Validate(validator, out brokenRules);

It still seems a little strange to have to know about the correct validator to use.  Elton wrote about a nice trick with Visitor and extension methods that I could use here.  I can use an extension method for the Order type to wrap the creation of the validator class:

public static bool ValidatePersistence(this Order entity, out IEnumerable<string> brokenRules)
{
    IValidator<Order> validator = new OrderPersistenceValidator();

    return entity.Validate(validator, brokenRules);
}

Now my client code is a little more bearable:

Order order = new Order();

IEnumerable<string> brokenRules;
bool isValid = order.ValidatePersistence(out brokenRules);

My Order class doesn't have any persistence validation logic, but with extension methods, I can make the client code unaware of which specific Validation class it needs.

A generic solution

Taking this one step further, I can use a Registry to register validators based on types, and create a more generic extension method that relies on constraints:

public class Validator
{
    private static Dictionary<Type, object> _validators = new Dictionary<Type, object>();

    public static void RegisterValidatorFor<T>(T entity, IValidator<T> validator)
        where T : IValidatable<T>
    {
        _validators.Add(entity.GetType(), validator);
    }

    public static IValidator<T> GetValidatorFor<T>(T entity)
        where T : IValidatable<T>
    {
        return _validators[entity.GetType()] as IValidator<T>;
    }

    public static bool Validate<T>(this T entity, out IEnumerable<string> brokenRules)
        where T : IValidatable<T>
    {
        IValidator<T> validator = Validator.GetValidatorFor(entity);

        return entity.Validate(validator, out brokenRules);
    }
}

Now I can use the extension method on any type that implements IValidatable<T>, including my Order, Customer, and Quote classes.  In my app startup code, I'll register all the appropriate validators needed.  If types use more than one validator, I can modify my registry to include some extra location information.  Typically, I'll keep all of this information in my IoC container so it can all get wired up automatically.

Visitor patterns are useful when they're really needed, as in the case of entity validation, but can be overkill sometimes.  With extension methods in C# 3.0, I can remove some of the difficulties that Visitor pattern introduces.

Kick It on DotNetKicks.com
Posted Oct 24 2007, 10:52 AM by bogardj
Filed under: ,

Comments

schambers wrote re: Entity validation with visitors and extension methods
on 10-24-2007 2:04 PM

Wow! what an excellent post.

I was following the discussion on the ALT.net group and this is a great compilation of the knowledge from that thread.

I was starting to employ this in my next project but didn't get to refactor to visitors yet so I havent used it.

You did a great job compiling all the information into one blog post. keep it up!!

CallContext wrote Validation implementation
on 10-24-2007 7:12 PM

Nifty way of implementing domain validation using extension methods and generics: www.lostechies.com/.../entity-validation-with-visitors-and-extension-methods.asp...

Joe Ocampo wrote re: Entity validation with visitors and extension methods
on 10-24-2007 11:39 PM

I am getting flashbacks of the CSLA!!!

dictionary » Entity validation with visitors and extension methods wrote dictionary &raquo; Entity validation with visitors and extension methods
on 10-25-2007 12:06 AM

Pingback from  dictionary &raquo; Entity validation with visitors and extension methods

Joe Ocampo wrote re: Entity validation with visitors and extension methods
on 10-25-2007 2:05 AM

I question the term Validator in relation to DDD.  Since the operation of the Validator seems to be a simple predicate based on business rule shouldn't the term Specification [Evans Pg227] be used instead?

quote » Entity validation with visitors and extension methods wrote quote &raquo; Entity validation with visitors and extension methods
on 10-25-2007 3:20 AM

Pingback from  quote &raquo; Entity validation with visitors and extension methods

bogardj wrote re: Entity validation with visitors and extension methods
on 10-25-2007 8:39 AM

@Joe

It looks like validators and specs are doing the same thing:  look at an object, and return true or false if it matches some specification.

The difference is specs are usually done for positive matching of individual customer-related specs, like "ColorSpec" or "GrossWeightSpec".  These specs can be collected through a composite spec to perform a search on the repository.

Validators however are used to perform negative matching of a collection of concerns in one single context.  For example, a single persistence validator performs all matching logic on every pertinent aspect of a single entity, not a collection of entities in a repository.

You know, great questions like these just deserve a post...

GrabBag wrote Specifications versus validators
on 10-25-2007 9:19 AM

Joe posed a great question on my recent entity validation post : I question the term Validator in relation

Tapio Kulmala wrote re: Entity validation with visitors and extension methods
on 11-04-2007 5:17 AM

You have an error in your examples :

   public bool IsValid(Order entity)

   {

       return BrokenRules(entity).Count() > 0;

   }

It very likely should be  :

   public bool IsValid(Order entity)

   {

       return BrokenRules(entity).Count() == 0;

   }

bogardj wrote re: Entity validation with visitors and extension methods
on 11-05-2007 8:55 AM

@Tapio

Thanks for the find!  Looks like my logic was backwards there...serves me right for not having tests

Brendan Enrick's Blog wrote Validating Entity objects
on 01-21-2009 1:23 PM

While working with an entity object I wanted to validate, I took some time to do some Googling. I was planning on creating an IValidator<T> interface so I could keep the validation separate from my classes, and I was going to implement this interface

Tobin Harris wrote re: Entity validation with visitors and extension methods
on 01-29-2009 7:15 AM

Nice idea. Where do you put validation checks for something that involves an entire set . For example, when validating a user, I want to make sure they have a unique email address. Do you pass a repository in to the newUser.Validate(...) (I feel this is bad)

bogardj wrote re: Entity validation with visitors and extension methods
on 01-29-2009 8:12 AM

@Tobin

For things like that, I'll put the validation on whatever handles the command/operation.  Things like unique email address aren't necessarily validation for a single entity, but rather a set of entities, so I'll put that validation in an operation that does UpdateUser or something similar.

Right now, we have 3 levels of validation: user input validation, operational consistency validation (business logic), and entity self-consistency validation.

Dmitriy Shvadskiy wrote re: Entity validation with visitors and extension methods
on 02-04-2009 9:39 AM

How do you reuse rules across validators? Say OrderPersistanceValidator and OrderReadyToShipValidator have common rule:

OrderShipDate >= Today

bogardj wrote re: Entity validation with visitors and extension methods
on 02-04-2009 5:31 PM

@Dmitriy

To be honest, that's not come up for me yet.  I can imagine some ways of doing it, maybe by breaking specific rules out into individual rules classes etc.

Chev wrote re: Entity validation with visitors and extension methods
on 02-16-2009 5:26 AM

really enjoyed the article.

Would it be possible to get a sample app?

bogardj wrote re: Entity validation with visitors and extension methods
on 02-16-2009 8:09 AM

@Chev

Well, unfortunately the _real_ code is in a proprietary NDA app, and the sample code from above I lost about a year ago.  Sorry...

Eugene wrote re: Entity validation with visitors and extension methods
on 10-20-2009 2:14 AM

Validator class should be static if you want to use extension method

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems