ObjectValidator and ruleset scope and nesting

Topics: Validation Application Block
Nov 25, 2008 at 4:52 PM
Edited Nov 25, 2008 at 4:54 PM
I am using EntLib 3.1 with the Policy Injection and VAB.

My problem is getting the ObjectValidator to work as advertised.

I have a BaseIssue object which implements an interface and has child properties which are also complex types.
Here is the structure cut down for brevity.

publicclassBaseIssue : IBaseIssue
{public IFaultyPart PartWithFault}

The FaultyPart object contains two properties which are of type Part

 public

 

class FaultyPart : IFaultyPart
{
public IPart MainPart
public IPart ComponentPart
}

public class Part : IPart
{public string PartCode}

I am using two rulesets applied to the BaseIssue and this works just fine.

I also have validator attributes applied on all the objects so that I get the following.

public class BaseIssue : IBaseIssue
{
    [NotNullValidator(Ruleset = "EditIssue")]
public IFaultyPart PartWithFault

    [SelfValidation(Ruleset = "EditIssue")]
    public void CheckChildObjects(ValidationResults vResults){

 

 

 

Validator childValidator;
if (mPartWithFault != null)
{
//Bookmark AAAAA
childValidator =
ValidationFactory.CreateValidator(mPartWithFault.GetType());
vResults.AddAllResults(childValidator.Validate(mPartWithFault));
}

}
//CheckChildObjects

} // end of class BaseIssue

public class FaultyPart : IFaultyPart
{
    [NotNullValidator] public IPart MainPart
    [NotNullValidator] public IPart ComponentPart

    [

SelfValidation]
    public void CheckChildObjects(ValidationResults vResults)
    {
        Validator childValidator;
        if (mMainPart != null)
            {
                //Bookmark BBBBB
                childValidator =
ValidationFactory.CreateValidator(mMainPart.GetType());
                vResults.AddAllResults(childValidator.Validate(mMainPart));
            }
    
} //CheckChildObjects
} // end of class FaultyPart

public class Part : IPart
{
[NotNullValidator] public string PartCode
}

 

 

Now I am not sure if I am doing this right but this works almost correctly except that the validation of the Parts does not occur.

 

 

That is, BaseIssue and FaultyPart main properties are validated.
I can chain from the BaseIssue into the FaultyPart to initiate validation of that dependant object, because I use the SelfValidation method.
See Bookmark AAAA above.
I had to use SelfValidation because simply decorating the PartWithFault property in the BaseIssue with the ObjectValidator attribute did nothing.

I tried to use the same trick from the SelfValidation of the FaultyPart to get the MainPart to be validated
(see Bookmark BBBBB) but so far as I can tell the SelfValidation method in the FaultyPart is not called.

Now, should I really be decorating the intefaces with the validation attributes rather than the concrete types?

And why does the ObjectValidator attribute not work?

I tried several combinations of code, putting the validators on the interface, not putting them on the interface, putting rulesets on everything, not putting rulesets on everything. Just could never get that particular attribute to do what is supposed to do.

Right now, the combination of ruleset and selfvalidation almost works, it does to two levels but not to three.

Anyone got some suggestions?

 

 

 

 

 

 

Nov 26, 2008 at 5:48 AM

Hi, I just put all the validation attributes in the interface types and it worked.  If this didn't worked for you, can you post your exact class definitions? It's quite confusing, some of the properties you pointed out were declared as fields.

public interface IBaseIssue
{
    [
NotNullValidator]
    [
ObjectValidator]
    IFaultyPart PartWithFault
    {
        get;
        set;
    }
}

 

 

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@avanade.com

 

Nov 26, 2008 at 7:09 AM
Ah, that helped.
Whilst in the process of preparing more complete sample code for you, I found that I had missed the [HasSelfValidation] attribute which should have been on the FaultyPart class. Once that was in place then the self validation worked to traverse the tree.
That old technique of simplification to isolate the problem really does work. Also a good night's sleep <g>.

But perhaps you could shed some light on the interface situation.
 
All my classes are inheriting from an interface, so should I really be putting the validators on the interface property declarations?
When do you do that?
I have included the more complete (though still abbreviated) code sample for you to see what elementary mistake I may be making.

The object graph is a little complex, basically I have this structure:

    B inherits from     -> A
    | has                        | has
     C                           D
                                    | has
                                    E

(Hoping the layout comes across)

It would be ideal if the ObjectValidator would work so that I could do away with the SelfValidation method.

Thanks for your help.

/* interfaces */

public interface IBaseIssue

{

IFaultyPart PartWithFault { get; set; }

}

public interface IBoatIssue : IBaseIssue

{

int BoatIssueId { get; set; }

IBoat BoatWithIssue { get; set; }

} //IBoatIssue

public interface IBoat

{

int BoatIssueId { get; set; }

string BoatModel { get; set; }

} //interface IBoat

public interface IFaultyPart

{

int FaultyPartId { get; set; }

IPart ComponentPart { get; set; }

IPart MainPart { get; set; }

} //interface IFaultyPart

public interface IPart

{

string PartCode { get; set; }

string PartDescription { get; set; }

} //interface IPart

/* end interfaces */

/* classes */

public class BaseIssue : IBaseIssue

{

[NotNullValidator(MessageTemplateResourceName = "BaseIssuePartWithFault",

MessageTemplateResourceType = typeof(Resources),

Ruleset = "EditIssue")]

public IFaultyPart PartWithFault

{

get { return mPartWithFault; }

set { mPartWithFault = value; }

}

[SelfValidation(Ruleset = "EditIssue")]

public void CheckChildObjects(ValidationResults vResults)

{

Validator childValidator;

if (mPartWithFault != null)

{

childValidator = ValidationFactory.CreateValidator(mPartWithFault.GetType());

vResults.AddAllResults(childValidator.Validate(mPartWithFault));

}

} //CheckChildObjects

} //class BaseIssue

 

[HasSelfValidation]

public class BoatIssue : BaseIssue, IBoatIssue

{

public IBoat BoatWithIssue

{

get { return mBoatWithIssue; }

set { mBoatWithIssue = value; }

}

 

[SelfValidation(Ruleset ="EditIssue")]

public void CheckBoatIssue(ValidationResults vResults)

{

Validator boatValidator;

boatValidator = ValidationFactory.CreateValidator(mBoatWithIssue.GetType());

vResults.AddAllResults(boatValidator.Validate(mBoatWithIssue));

}

} //class BoatIssue

public class Boat : IBoat

{

[StringLengthValidator(1, 25,

MessageTemplateResourceName = "BoatBoatModel",

MessageTemplateResourceType = typeof(Resources))]

public string BoatModel

{

get { return mBoatModel; }

set { mBoatModel = value; }

}

} //class Boat

[HasSelfValidation]

public class FaultyPart : IFaultyPart

{

[NotNullValidator(MessageTemplateResourceName = "FaultyPartMainPart",

MessageTemplateResourceType = typeof(Resources))]

public IPart MainPart

{

get { return mMainPart; }

set { mMainPart = value; }

}

[SelfValidation]

public void CheckChildObjects(ValidationResults vResults)

{

Validator childValidator;

if (mMainPart != null)

{

childValidator = ValidationFactory.CreateValidator(mMainPart.GetType());

vResults.AddAllResults(childValidator.Validate(mMainPart));

}

} //CheckChildObjects

} //class FaultyPart

 

public class Part : IPart

{

[NotNullValidator(MessageTemplateResourceName = "PartPartCode",

MessageTemplateResourceType = typeof(Resources))]

[StringLengthValidator(1, 30,

MessageTemplateResourceName = "PartPartCodeLen",

MessageTemplateResourceType = typeof(Resources))]

public string PartCode

{

get { return mPartCode; }

set { mPartCode = value; }

}

} //class Part

 /* end classes */

Nov 26, 2008 at 7:19 AM
Edited Nov 26, 2008 at 8:22 AM
Yes, it's better to put the attributes in the interface.  I'll use your classes and try it out to see why you're not able to use the ObjectValidator.

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@avanade.com

 

 

 

Nov 26, 2008 at 8:27 AM
Now I have another interesting situation.

My BaseIssue has a property which is a collection and I have applied the ObjectCollectionValidator to it thusly.

[

ObjectCollectionValidator(typeof(IAttachment), Ruleset = "EditIssue")]
public IAttachmentCollection Attachments{
get { return mAttachments; }
set { mAttachments = value; }
}

When I put the validators on the attachment items (of type IAttachment) , that is directly on the concrete class, it does not work.
If I put the validators on the interface then it _does_ work.
But only if I don't use the ruleset!

 

 

public class AttachmentCollection : List<Attachment>, IAttachmentCollection{}

public class Attachment : IAttachment{
public string FileName{
get { return mFilename; }
set { mFilename = value; }
}

public interface IAttachment{
[StringLengthValidator(1, 50
//,MessageTemplateResourceName = "AttachmentFileName"
//,MessageTemplateResourceType = typeof(Resources)
//,Ruleset = "EditIssue")] string FileName { get; set; }
}

Now I have a situation where I have some validators on classes and some on interfaces, not good. Will have to go one way or the other.
I am using a resource file to hold the message templates so I need to put in them in only one assembly.

 

Nov 26, 2008 at 8:31 AM
Have you tried out putting all the validator attributes in the interface including the [ObjectValidator] attribute?  It should work.  It also provides a clean implementation rather than decorating all your inheriting classes with the attributes.   For validators belonging to a ruleset, make sure to pass the name of the ruleset when trying to validate an object.  In your case, your call to validate an IBaseIssue object (or creating a validator for an IBaseIssue object) should include the "EditIssue" as a parameter.   


Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@avanade.com
Nov 26, 2008 at 10:03 AM
Ok, I have moved all my validators into the interfaces, have put objectvalidator attr where necessary and commented out the SelfValidation attr.

I have also put the same ruleset on every validator.

Now nothing works. (sigh).

Was wondering about how I am calling the Validate method , is this right?

Validator baseValidator =

ValidationFactory.CreateValidator( vThisIssue.GetType(), "EditIssue");
ValidationResults results = baseValidator.Validate(vThisIssue);


Considering that my variable vThisIssue conforms to the interface IBaseIssue, but could be either a PartIssue or a BoatIssue (in my sample it is a BoatIssue) is this the right way to call the Validate method?

At the moment it runs with no errors but does not validate. When I had the validators in the concrete classes they all validated.

 

Nov 26, 2008 at 10:19 AM
Ok making some progress here.

The Validate method call does seem to be wrong.
I changed it to 
baseValidator = ValidationFactory.CreateValidator(typeof(IBaseIssue), "EditIssue");

and the validations for BaseIssue are run (which is good) but none of the properties which have the ObjectValidator attr are being validated (which is bad).

Also considering that I need to deal with the varying type of Issue this solution is not very helpful.
Nov 26, 2008 at 10:39 AM
Weirdness.

If I validate using the IBaseIssue type, then all the validations (except the ObjectValidators) which are in the interface are checked.

If I validate using the BaseIssue type, then none of the validations are checked but the SelfValidation method in BaseIssue is called.

If I validate using the BoatIssue type (which is what I desire) then nothing is checked. BoatIssue is declared as 

public

 

class BoatIssue : BaseIssue, IBoatIssue

 

Considering that the BaseIssue inherits the IBaseIssue interface this seems just plain wrong.

I will just have to go back to putting the validators on the concrete classes.
Nov 26, 2008 at 10:59 AM
I tried this and it works, including the ObjectValidator attribute.

Validator<IBaseIssue> validator = ValidationFactory.CreateValidator<IBaseIssue>("EditIssue");

It's always good to use the interface types when creating validators.  

"If I validate using the BaseIssue type, then none of the validations are checked but the SelfValidation method in BaseIssue is called."
 - What object are you passing into the Validate method? The interface type of the concrete one?  Same question goes for the BoatIssue type.


Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@avanade.com
Nov 26, 2008 at 11:06 AM
Edited Nov 26, 2008 at 11:10 AM
Right I think I have sussed this now.

The ObjectValidator attribute accepts a ruleset but this is not passed down to the object which is being checked.

My BoatIssue has a property 

public

 

IBoat BoatWithIssue

and the interface for the IBoatIssue has a validator
public interface IBoatIssue : IBaseIssue
{
[
ObjectValidator(Ruleset = "EditIssue")]
IBoat BoatWithIssue { get; set; }
}

The BoatWithIssue will be validated so long as the validators on _it_ do NOT have any rulesets applied.

So now what I have is all my validators in the interfaces(good) and am using ObjectValidator(good), bit of a mess with the rulesets but I can cope.

Now what I had to do was modify the way the Validation was being kicked off.

Originally I had

baseValidator = ValidationFactory.CreateValidator(vThisIssue.GetType(), "EditIssue");

But it seems that the VAB cannot understand the inheritance tree so I have had to go with being more explicit.

My variable vThisIssue inherits from IBaseIssue but is actually of type BoatIssue which in turn inherits from IBoatIssue.

So my code now has to say

// do the validation for the base interface type
baseValidator = ValidationFactory.CreateValidator(typeof(IBaseIssue), "EditIssue");
vThisIssue.ValidationMessages = baseValidator.Validate(vThisIssue);

// now do the validation for the interface type of this particular instance
baseValidator =
ValidationFactory.CreateValidator(typeof(IBoatIssue), "EditIssue");
results = baseValidator.Validate(vThisIssue);
vThisIssue.ValidationMessages.AddAllResults(results);

 

 

 

This is far from ideal and I am sure that there is a bit more fiddling to do.

 

 

 

Nov 26, 2008 at 12:42 PM
Edited Nov 26, 2008 at 12:46 PM

The Validation Application Block maps validators to types.  If you create a validator of type IBaseIssue, it will find all validators attached to that type but it won't find the validators in the IBoatIssue.  Sorry, I was lost for a while thinking this was just about the ObjectValidator not working for you. 


Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 26, 2008 at 12:45 PM
Edited Nov 26, 2008 at 12:49 PM
Right, I have a satisfactory solution now.

1. Validators on the interfaces.
2. ObjectValidator attributes applied with ruleset.
3. Any object which is validated through the ObjectValidator action must not have any ruleset on any validators.
3. The CreateValidator method is called thus:
 
ValidationFactory.CreateValidator(vThisIssue.GetType(), "EditIssue")
In my example this has a type of BoatIssue.
Note that this means that the concrete class type is used, not the interface type or base type from which it is inherited and derived.
4. The BoatIssue concrete class is marked with the [HasSelfValidation] attribute and a CheckSelf() method marked with the [SelfValidation] attr.
5. The CheckSelf() method looks like this:
[SelfValidation(Ruleset = "EditIssue")]
[
SelfValidation(Ruleset = "NewIssue")]
public void CheckSelf(ValidationResults vResults)
{
Validator validator;
if (this.GetType() != typeof(BaseIssue))
{validator =
ValidationFactory.CreateValidator(typeof(IBaseIssue), "EditIssue");
vResults.AddAllResults(validator.Validate(
this));}

 

 

// need to manually add the validation of self using the interface
validator = ValidationFactory.CreateValidator(typeof(IBoatIssue), "EditIssue");
vResults.AddAllResults(validator.Validate(
this));
}
//CheckSelf

 

 

 

 

Note that this technique was suggested on Tom Hollander'sblog.
http://blogs.msdn.com/tomholl/archive/2008/03/02/polymorphism-and-the-validation-application-block.aspx

Now at last I can get back to other things.