validation of derived class in an array of base class

Apr 26, 2011 at 2:48 PM

We haven't been able to figure out how to implement validation of derived class objects in an array of base class. How would we implement this example:

public class Vehicle
{
}

public class Car : Vehicle
{
}

public class Truck : Vehicle
{
}

public class Person
{
       Vehicle[] OwnedVehicles {get;set;}
}

How would we implement the PersonSpecification to use CarSpecification or TruckSpecification depending on the type of the object in the OwnedVehicles array?

Coordinator
Apr 26, 2011 at 4:36 PM

This bug has been identified and addressed since the posted release.  You can checkout the source and build the library yourself to get the version that includes the fix.

Thanks for the post!

Apr 26, 2011 at 7:47 PM
Edited Apr 26, 2011 at 7:59 PM

I am unclear on how to implement this. I just got latest code and couldn't find a unit test for this case. So, here is what I tried :-

public class PersonSpecification : Validates<Person>
{
Check(p => p.OwnedVehicles).Required().CountGreaterThanEqualTo(1).And.ForEachSpecification<Vehicle>(); // --> Does not validate Car or truck
}

And :-

public class PersonSpecification : Validates<Person>
{
              Check(p => p.OwnedVehicles).Required().CountGreaterThanEqualTo(1).
                                And.ForEachSpecification<Vehicle>().
                                And.ForEachSpecification<Car>().
                                And.ForEachSpecification<Truck>(); // --> invalid cast exception
}

public class VehicleSpecification: Validates<Vehicle>
{
             Using<Car, CarSpecification>(); // this line does not help ...
}

public class CarSpecification: Validates<Car>
{

}

public class TruckSpecification : Validates<Truck>
{

}

Any help would be greatly appreciated.

Thanks

Coordinator
Apr 27, 2011 at 1:37 AM

NoFear,

I'm pretty familiar with this polymorphic scenario as I've been dealing with it a lot in our domain recently. Taking a look at your setup, here's my feedback.

1. The

Using<Car, CarSpecification>();
in VehicleSpecification is misplaced. The Using is a way to implement inheritance of specifications and should follow the inheritance chain for the type you are validating. In your case, since Car inherits from Vehicle, you should add a
Using<Vehicle, VehicleSpecification>()
to CarSpecification and TruckSpecification, if you want to include the rules from VehicleSpecification in addition to the rules for CarSpecification and TruckSpecification while validating Car and Truck.

2. Unfortunately, because this is newer functionality, I haven't updated the Wiki yet. But since the OwnedVehicles collection can contain multiple types, you can't really specify a specification to use for each type that might be in the collection (Car, Truck, ect). So, if you use the following syntax, it will leave it up to the ValidationCatalog to use the default specification for each item in the collection:

Check(p=>p.OwnedVehicles).Required().ForEachSpecification();

I'll be sure to update the Wiki with this new guidance.

Apr 29, 2011 at 11:01 PM

Doh!!! Of course, obviously ... I think it didn't work in the last build and I didn't try it in the new one ... or ...

Anyways, on to the next question:

I am trying to figure out how to validate a distinct count of each type of vehicle ... lets say you can have at most 2 cars and 2 trucks ... here is what I tried and I think I might need a separate validator but I wanted to run it by you ...

Check(p => p.Items).Required().CountGreaterThanEqualTo(1)
                .And.ForEachSpecification()
                .And.Expect(ValidateCount, "Invalid Number of items");
private bool ValidateCount(Person person, Vehicle[] ownedVehicles)
{    
	if(ownedVehicles == null) return; //maybe
var vehicleCount = from vehicle in ownedVehicles		
                         group vehicle by vehicle.GetType()
                         into g
                         select new {Category = g.Key, Count = g.Count()};
foreach(var count in vehicleCount)
{
	// check each vehicle count against a repository value or constant and report a validation failure
}
}

 So, I couldn't find a way to report a validation failure like "You cannot have more than 2 cars" or "you cannot have more than two trucks" or both. Is the best way for me to write a custom validator ... here is what I did:

 public class PersonVehicleValidator<T> : RuleValidator<T, Vehicle[]>
        where T : Person
    {
 public override bool Validate(RuleValidatorContext<T, Vehicle[]> context,
                                      SpecificationContainer specificationContainer, ValidationNotification notification)
        {
	notification.Errors.Add( "TODO" ); //Maybe add errors here ...
return !validationFailed; // or something
}

}

public static class PersonVehicleValidatorExtensions
    {
        public static ActionJoinBuilder<T, Vehicle[]> IsValidCount<T>(
            this IRuleBuilder<T, Vehicle[]> expression)
            where T :  Person
        {
            expression.RegisterValidator(new PersonVehicleValidator<T>());
            return expression.JoinBuilder;
        }
    }

 

 

Coordinator
Apr 30, 2011 at 12:42 AM
Edited Apr 30, 2011 at 5:35 PM

Glad that it worked out for you. Regarding your current challenge, I think you could avoid creating a custom rule by putting a Check on an expression instead of a property, like this:

 

Check( c => c.Vehicles.Count(c => c is Car), "Car count" )
	.Optional()
	.LessThanEqualTo(2);
Check( c => c.Vehicles.Count(c => c is Truck), "Truck count" )
	.Optional()
	.LessThanEqualTo(2);

This would yield an error string of Car count must be must be less than or equal to 2.

If you want a different message, drop overriding the property name and add

.With ( m => m.Message = "custom text here");

For more on this approach, check out http://specexpress.codeplex.com/wikipage?title=Validating%20Expressions&referringTitle=Documentation