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.
11 comments:
Thanks for the great post. Is there any reason why a traditional visitor pattern should be used, rather than calling the validator routine directly?
e.g.
public static bool Validate<T>(this T entity, out IEnumerable<string> brokenRules)
where T : IValidatable<T>
{
IValidator<T> validator = Validator.GetValidatorFor(entity);
return validator.Validate(entity, out brokenRules);
}
Need to move Validate(..) to IValidator<T> instead of Order for this to work. That way the Order object doesn't know anything about validator or being validatable etc.
Are there advantages of Visitor over this approach, or is it 6 of one, half-dozen of the other?
@David
That's definitely logically equivalent. If you move the validate method over, you'll still need the IValidatable<T> interface as the extension method is constrained by that interface. It would just become a marker interface then.
The plus side of keeping the validate method on the entity is that you're not forced to use the extension method to perform validation. To me, extension methods are helper methods, but shouldn't necessarily be the only point of access to behavior.
Oops, my code snippet copied the extension method part. I was basically referring to dropping the extension method and putting all the logic in a Validator class, which tracks and calls IValidator<T> objects for any T (not just IValidatable).
This separates responsibilities, but admittedly is less fun because it doesn't let me play around with extension methods :)
A great post.
Just a simple question.
Why do you mark GetValidator a public ?
Who use this method outside Validator class?
Many thanks
claudio
@Makka
The Validator class is a Registry, which has two methods, "Register" and "Get". For convenience's sake I put the extension method on the Validator class, but I would usually define it elsewhere.
Extension methods probably should go in their own namespace, like "Validation.Extensions" or something similar, so clients can opt-in to the extension methods they want. If methods just start showing up unexpectedly, it can get confusing and annoying to deal with.
Hi, could you please add the Generic Validation class + sample usage into an archive file for download?
I tried to add this code and probabley did something wrong because I can't build the project.
Thanks.
Shay
Wow, this is just what I'm looking for. I also tried to cut and paste and ran into some issues. I'm sure it's all me but if you had a sample solution to help me get started that would be awsome.
@Second anon
Sure, contact me through the link up there and I'll send ya some more complete code.
Hi I accidently coded up something similar. But to me both your and my solution have a problem in a specific situation. Toy Example(WCF):
Assume you have a webservice about handling shapes. You have DataContracts: Circle, Squire, Triangle etc. All derive from a base class Shape. At the serverside you want to draw the shapes, log , paint them red, let them dance whatever. All concerns you do not want to show in your datcontract as in your blogpost. Now I have a webservice call that accepts a Shape in an Message object. On Message there is a shape property with type Shape(the base class). In the servicee method you now want to handle the message but all you know without trying to downcast is that you have Shape. Being in hurry i choose downcasting for now but i would rather solve it. Any suggestions?
I Solved the problem for VB.NET
by turning strict off in one place.
It wrote a ShapeAcceptor class that accepts triangle, circles and squares in private Accept methods. There is a public AcceptShape method that accepts a shape as an argument.
It calls a private AcceptAny method that accepts an object as Argument.
It calls the private Accept methods. Calling the ShapeAcceptor class with anything else than a shape is still possible due to strict off but the casting exception gets thrown in the AcceptAny method. I still have to figure out whether it is possible to turn this into a compile time error by using subclasses.
I wrote nice blog entry with example code on http://blog.nicenemo.net/2009/10/vistor-pattern-with-extension-methods.html
Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!
Post a Comment