(Enterprise Validation Block, Interfaces) => BIG Trouble
I had a first look at Microsoft’s Enterprise Validation Block 4.1 today and trashed it before I even got really started. The reason: The thing doesn’t really support OO-architectures that rely on interfaces / polymorphism.
I ran my test with this simple interface / implementation:
public interface IModel { [StringLengthValidator(1, 5, MessageTemplate = "First rule failed.")] string First { get; set; } [StringLengthValidator(1, 5, MessageTemplate = "Second rule failed.")] string Second { get; set; } [StringLengthValidator(1, 5, MessageTemplate = "Third rule failed.")] string Third { get; set; } } public class Model : IModel { public string First { get; set; } public string Second { get; set; } public string Third { get; set; } }
Now look at this test:
public void Test() { Model model = new Model(); var result = Validation.Validate(model); foreach (var validationResult in result) { Console.Out.WriteLine(validationResult.Message); } }
The code above won’t output a single validation error, because it only checks the Model class for validation attributes. And there are none – the rules have been declared in the IModel interface. My bad.
The code below, on the other hand, works. Notice that the model variable is declared using the interface type:
public void Test() { IModel model = new Model(); var result = Validation.Validate(model); foreach (var validationResult in result) { Console.Out.WriteLine(validationResult.Message); } }
Now, it gets really ugly when you’re having attributes at different places. After this initial test, I moved the third validation rule into the implementing Model class and removed the Third property from the interface:
public interface IModel { [StringLengthValidator(1, 5, MessageTemplate = "First rule failed.")] string First { get; set; } [StringLengthValidator(1, 5, MessageTemplate = "Second rule failed.")] string Second { get; set; } } public class Model : IModel { public string First { get; set; } public string Second { get; set; } [StringLengthValidator(1, 5, MessageTemplate = "Third rule failed.")] public string Third { get; set; } }
As a result, validating for the IModel interface returns two validation errors, and validating for the Model class reports a single error for the third rule. Now think about handling scenarios where your class implements several interfaces that may provide validation rules. Ouch.
Support for inheritance was the absolute first thing I tested. Frankly, it is beyond me how this library could make it into production over a year ago with this kind of behavior.
This not only promotes bad design, it’s also dangerous as hell because refactoring silently breaks your validation.
Actually, it is possible to validate inherited class and still use validators on the base class/interface. I had the same problem before and blogged about it here: http://codemind.blogspot.com/2008/08/are-you-valid.html
You should not use Validation facade, but instead create an instance of Validator class by using ValidationFactory. So, instead of:
var result = Validation.Validate(model);
you should use:
var validator = ValidationFactory.CreateValidator(typeof(Model));
var result = validator.Validate(model);
Not really sure why the Validation facade doesn’t have the support for inheritance. It’s definitely confusing.
Regards,
Miroslav
Miroslav,
I tried that already. It might work with abstract base classes, but it definitely doesn’t with interfaces.
Here’s my snippet:
public void Test()
{
Model model = new Model();
var validator = ValidationFactory.CreateValidator(typeof(IModel));
var result = validator.Validate(model);
foreach (var validationResult in result)
{
Console.Out.WriteLine(validationResult.Message);
}
}
…which produces the same output as the static validation method (the third rule is still missing):
First rule failed.
Second rule failed.
…this is not really surprising: If you look at the source of the Validation class, you can see that it does nothing else than invoking the factory itself. So the code is equivalent.
Cheers,
Philipp