Tom Hollander's blog

patterns, practices and pontification

Polymorphism and the Validation Application Block

Polymorphism and the Validation Application Block

  • Comments 6

The Validation Application Block is a useful piece of technology, but unfortunately it doesn't get along too well with that slippery character called Polymorphism. This is because you need to tell the block the exact type in which it should look for validators - it won't automatically figure out what the real type of your instance is.

For example, consider the following type definitions:

public class BaseClass
{
    [StringLengthValidator(1, 5)]
    public virtual string A
    {
        get;
        set;
    }
}

public class DerivedClass : BaseClass
{
    [StringLengthValidator(1, 5)]
    public string B
    {
        get;
        set;
    }
}

Now if I validate an instance of DerivedClass using Validation.Validate<DerivedClass>, everything works as expected. However if I validate my DerivedClass instance using Validation.Validate<BaseClass>, it will completely ignore property B and its validators. And that is rarely a good thing.

Of course if you know that your instance is a DerivedClass, you can simply call Validation.Validate<DerivedClass>. But the whole point of polymorphism is that you normally won't know which specific concrete type you have. Luckily there is a simple solution, which is to use one of the overloads of ValidationFactory.CreateFactory that lets you specify the target type using a non-generic type parameter, as in the following example:

[TestMethod]
public void ValidateActualTypeWithInvalidDerivedClass()
{
    BaseClass b = new DerivedClass() { A = "Valid", B = "Invalid" };
    Validator validator = ValidationFactory.CreateValidator(b.GetType());
    ValidationResults results = validator.Validate(b);
    Assert.IsFalse(results.IsValid);
    Assert.IsTrue(results.ToArray<ValidationResult>().Length == 1);
}

This works well if you want to want to manually validate a specific instance. However things get even more tricky if you have a hierarchy of types that you want to validate with the help of the Object Validator. Consider the following type:

public class ContainerClass
{
    [ObjectValidator]
    public BaseClass NestedClass
    {
        get;
        set;
    }
}

In this case I can set my NestedClass property to a BaseClass, a DerivedClass, or some other subclass which I haven't thought of yet. Ideally, whenever I ask the VAB to validate my ContainerClass, the rules should kick in for whatever flavour of class I choose to set in my NestedClass property. It would probably be possible to build a supercharged ObjectValidator attribute that does this, but failing that there isn't any way that I know of to apply my previous trick to get the block to look at the concrete type for validation rules.

However I did stumble across the following hack that seems to work well. I used "self validation" to create an additional method on the base class which effectively delegates validation to the derived class:

[HasSelfValidation]
public class BaseClass
{
    [StringLengthValidator(1, 5)]
    public virtual string A
    {
        get;
        set;
    }

    [SelfValidation]
    public virtual void Validate(ValidationResults results)
    {
        if (this.GetType() != typeof(BaseClass))
        {
            Validator validator = ValidationFactory.CreateValidator(this.GetType());
            results.AddAllResults(validator.Validate(this));
        }
    }
}

The "if" test in the self-validation method is necessary to prevent an endless loop with the self-validation calling itself. With this hack in place, the following test now passes. In fact, it's also possible to validate a DerivedClass using Validation.Validate<BaseClass> as well!

[TestMethod]
public void ValidateContainerTypeWithInvalidDerivedClass()
{
    ContainerClass c = new ContainerClass() { NestedClass = new DerivedClass() { A="Valid", B="Invalid"} };
    ValidationResults results = Validation.Validate<ContainerClass>(c);
    Assert.IsFalse(results.IsValid);
    Assert.IsTrue(results.ToArray<ValidationResult>().Length == 1);
}

I admit this hack is a little ugly and ideally wouldn't be necessary. However it's also very simple and does not require any changes to the Validation Application Block source code, so it seems to be a good solution for now.

  • The situation is getting worse since I'm working with domain objects using interface. For example, I have an Account class inherits from IAccount interface. Then I also have Person class inherits from Account and IPerson interface, and then IPerson itself inherits from IAccount. If I validated an IPerson object then nothing happens. I usually need to validate it against a Person type.

    Shouldn't VAB support this out-of-the-box instead of introducing another workarounds and so on? How about v4.0?

    I also found VAB doesn't work with Nullable<T> type, for example DateTime? when validated using DateTimeRangeValidator. Of course, it could be "worked-around" again, but then I suddenly think that's just not right anymore.

    Lastly, why doesn't WinForms Integration's ValidationProvider support validation triggered by method marked with [SelftValidation] (I haven't checked other ValidationProviders though)?

    Thanks!

  • Thanks Maximilian - this is good feedback.

    Re interfaces: good point - I suspect there is no good way of supporting this today since my [HasSelfValidation] trick won't work.

    Re v4.0 plans - I'm no longer in the p&p team so I can't comment. Try http://codeplex.com/entlib or http://blogs.msdn.com/agile.

    Re Nullable types: this isn't a limitation in the VAB architecture per se - but it is a limitation with some of the validators. The RelativeDateTimeValidator, for example, derives from Validator<DateTime>, so it can't deal with nulls. You could easily build a validator deriving from Vaildator<DateTime?> which would do what you want. That said I can see that it would be nice if this were supported out-of-the-box.

    Re SelfValidation + Winforms integration: Both the ASP.NET and Winforms integration layers work in a different way to what most people expect. Instead of creating an instance of a type and validating it, they actually inspect the type definition looking for validators on properties, and validate the data in the controls using those validators. This significantly simplifies things since we don't need to figure out how to create and populate instances, however a side effect is that self validation won't work since that requires executing code in the instance.

    Tom

  • How could the Validation Application Block be changed to support polymorphism?  I haven't dug into the source, but my guess is that it's using reflection to load these types and invoking methods.  If that's the techinque - is that able to be polymorphic-friendly?  I haven't tested this using CSLA's validation logic, but I'll try to replicate your tests and let you know.

  • Hi Jeff -

    The reflection classes let you be very explicit about inspecting the complete hierarchy of types and interfaces, so I have no doubt that it would be possible to make it work better than it does now. The tricky bit is figuring out exactly how one would want it to behave. To be honest we realised that there were limitations in our design when we built the block, however we did not have the time and resources to update the design with polymorphism in mind. I for one would love to see this addressed in v4.

    Tom

  • You're not kidding "The tricky bit is figuring out exactly how one would want it to behave".

    My head starts to hurt when I think about which attributes should still be applied to an overriden method.  What if you want to override the attributes as well as the method?  What if you want to define an abstact method with some attributes and leave the code to the concrete class? Ow, quit it.

  • I am having a look at the Standalone Application Validation Application Block (I'm not sure it supports what's on the latest EntLib) but anyway.

    Is it possible with VAB to create a Validator class that is a composite of several validators without having to use any sort of external configuration ?

    For instance :

    public class Foo {

     public string Title {get;set;}

    }

    public class TitleIsUpperCase : Validator<String> {

    // ??

    }

    public class TitleIsFoo : Validator<String> {

    // ??

    }

    public class CheckTitleForARuleSet : Validator<Foo> {

    // ??, I would like to add TitleIsUpperCase and TitleIsFoo to Title property

    }

Page 1 of 1 (6 items)