Validating collections

Posts   
 
    
mafti
User
Posts: 38
Joined: 27-Oct-2004
# Posted on: 29-Dec-2004 14:44:20   

Hi,

i have read a collection from a table.


Dim tmp_deelnemers As New Tmp_deelnemerCollection
tmp_deelnemers.GetMulti(nothing)
tmp_deelnemers.ValidatorToUse = New SPBS.ValidatorClasses.DeelnemerEntityValidator
tmp_deelnemers.SaveMulti()

but then i get a cast error.

this means that for a collection.validatortouse is not for the entities in the collection, but the collection itself?

my validator class:

    Public Class DeelnemerEntityValidator
        Implements IEntityValidator

        Public Function Validate(ByVal containingEntity As Object) As Boolean _
         Implements IEntityValidator.Validate

            Dim deelnemer As Tmp_deelnemerEntity = CType(containingEntity, Tmp_deelnemerEntity)
            ' valideren de handel

            Return True
        End Function
    End Class
mafti
User
Posts: 38
Joined: 27-Oct-2004
# Posted on: 29-Dec-2004 14:50:17   

hmm,

i only can validate on fields instead of the whole entity in collections???

so, my validator should be implementing IValidator???

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 29-Dec-2004 16:03:12   

ValidatorToUse is used to set the ValidatorToUse property of new entities in the collection when you call GetMulti(). It is not used for validation of collection contents as they're not seen as a unit which can be validated, entities are validated separately.

Frans Bouma | Lead developer LLBLGen Pro
bmoeskau
User
Posts: 54
Joined: 15-Jun-2005
# Posted on: 15-Jun-2005 22:10:59   

Can you clarify what this means? I am still confused. I searched for GetMulti in the help and it seems to be specific to the self-servicing model? I am using the adapter model and have a standard entity collection that I want to "validate" (i.e., apply a validator to each entity in the collection and aggregate all validation errors into a single list to return to the client).

I am confused by the same thing as the original poster. Am I supposed to create a class to implement IValidator first, then somehow use that class to hook up my entity IEntityValidators to the entity objects in the collection? What is the recommended approach for validating entities in collections?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 16-Jun-2005 10:46:26   

bmoeskau wrote:

Can you clarify what this means? I am still confused. I searched for GetMulti in the help and it seems to be specific to the self-servicing model? I am using the adapter model and have a standard entity collection that I want to "validate" (i.e., apply a validator to each entity in the collection and aggregate all validation errors into a single list to return to the client).

I am confused by the same thing as the original poster. Am I supposed to create a class to implement IValidator first, then somehow use that class to hook up my entity IEntityValidators to the entity objects in the collection? What is the recommended approach for validating entities in collections?

In OO you normally use the principle of 'delegation', i.e.: validating an entity can be done best by the entity itself. (for rules which don't require data from outside the entity of course).

You can do that by plugging in an IEntityValidator object you write yourself in the entity object, which validates the entity when its Validate() method is called.

Though you can also opt for a class which contains your business rules and which exposes methods which accept an entity collection, and in which you validate your collection's entities one by one. This approach is often used when the rules require data from outside the entity to validate and is sometimes called the 'Manager' approach ('CustomerManager' etc.). Like 'Customer.IsGoldCustomer' is true when the customer has bought n orders in the last m months, which thus requires reads of order data.

Every entity gets already its own IValidator classes. These are used for FIELD validations, like 'ID > 0'. IEntityValidator classes aren't generated, you have to create these yourself. These should be used for ENTITY validation, like OrderDate <= ShippingDate.

They aren't generated because not everyone needs them. The field validators are generated and are called every time you set a field value.

Ok, back to your entity collection. It's more of a 'semantical' issue. Do you see your entity collection as a unit or just as a container with individual units? If you see it as a single unit (and I think you do, as you want to grab all errors in a single bag), you should use the manager approach. If you see them as individual units, use the IEntityValidator approach.

Frans Bouma | Lead developer LLBLGen Pro
bmoeskau
User
Posts: 54
Joined: 15-Jun-2005
# Posted on: 17-Jun-2005 01:03:42   

Now that I think about it, I think I'm going to change my basic approach to this page. It is a user preferences page and the backing data is currently implemented as a relationship table that relates a user table and a generic preferences table by IDs. The problem this is causing me is that I want a set of preferences for a given user to function as a single entity, but currently each individual UserPreference is a separate entity that I have to retrieve through an entity collection. Loading and saving is working OK, but I don't have an easy way to validate and pass errors back to the page as a single unit. On other pages we have been accumulating errors within the entity validator classes and letting the original caller that passed in the entity simply read the error collection from its reference if the save call fails. On this page, every single field/entity would have its own validator and errors, not really what I want.

After giving it some more thought, maybe I should simplify to a single preferences table with a user id and a bunch of preference value columns. This would give me my true preference entity and life would be good. I have implemented preferences via relationship tables in the past as it is denormalized a little more and allows you to maintain standard preference descriptions and that type of thing, but that's not really a requirement. Do you have any thoughts on implementing preferences like this in general?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Jun-2005 11:49:53   

How preferences are stored is typical for the app I think: the preferences are complex (i.e.: not name-value pairs, but whole hierarchical things) it's wise to keep it normalized. If your preferences are name-value pairs, a denormalized setup is preferred.

Frans Bouma | Lead developer LLBLGen Pro
bmoeskau
User
Posts: 54
Joined: 15-Jun-2005
# Posted on: 19-Jun-2005 07:19:13   

So maybe what I'm missing is the best approach for validating and passing errors back for groups of entities from normalized table relationships. Take any example like this: preferences, addresses, order line items, etc. You would have an IEntityValidator for each discreet class instance, and you would have a collection that contains all of the entities. If I'm loading the collection in the UI and then passing it to the BL for processing, what is the proper strategy for validating and getting errors back to the UI? Again, when dealing with a single entity object, I am attaching the IEntityValidator and accessing its error collection from the UI's reference to the business object to display errors. Using an entity collection, I don't see how I would accomplish this through the IValidator interface.

Maybe some code would be more clear. Here's an example of the UI code pattern I'm currently using to do validation for single entities:


entity.foo = "X";
entity.foo2 = "Y";

if (MyManager.Save(entity)
{
    DisplayMessage("Success");
}
else
{
    DisplayBrokenRules(((MyValidator)entity.EntityValidatorToUse).BrokenRules);
}

We have standardized on all of our IEntityValidators having a BrokenRules collection and sharing some base validation code. This is a convenient and standard way for us to get the error messages back from the BL without having to have the broken rules collection as the return value of every Save call.

In the preferences example, I have something more like this:


EntityCollection prefs = MyManager.GetPreferences();
foreach (PreferenceEntity pref in prefs)
{
    switch (pref.Name)
    {
        case "foo":
            pref.PreferenceValue = fooDropdown.SelectedValue;
            break;
        // etc...
    }
}

if (MyManager.Save(prefs)  //Note that this is the collection
{
    DisplayMessage("Success");
}
else
{
    // DisplayBrokenRules(((MyValidator)prefs.EntityValidatorToUse).BrokenRules);
}

I would like to follow the same general validation pattern, but of course the entity collection implements IValidator, not IEntityValidator. I could implement IValidator and give it a BrokenRules collection, but I'm unclear how I would actually perform the validation. The help says that the parameters to IValidator are the field index and the field value of a single entity field. What I would like is to be able to have the validation call on the collection loop through each contained object, calling Validate on each one, then returning the aggregated BrokenRules collection. Should I just ignore the parameters to IValidator and implement a loop as I'm describing? I guess the interface of IValidator is confusing me in the context of a collection object since it seems specific to a single entity.

Sorry for the lengthy post for something that should be pretty simple. I really appreciate your help.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 20-Jun-2005 11:55:39   

IValidator is for per-field validations (CustomerID > 0), (and was originally the only validator), IEntityValidator is for entity validation (OrderDate <= ShippingDate). As IValidator couldn't be changed, IEntityValidator was created.

The EntityCollection class accepts an IValidator to set in each NEW entity instance it creates itself (for example by using a grid) It doesn't have an EntityValidatorToUse property yet, which was an oversight and will be added in 1.0.2005.1, though that's again the validator instance to be SET in NEW entities.

There's no collection wide validation mechanism build in, (as there's no cross-entity validation build in), yet.

The main issue with collection wide validation is: you've to propagate the results upwards. So, if you validate the customer.Orders[n].OrderDetails collection, and an OrderDetail is invalid, is customer then also invalid? If so, the validator of Customer has to report invalid, though it can, by performing the validation DOWNwards to orderdetail, if you want to.

However, it then gets a bit complicated, because you mix full-graph validation with 'per-entity' validation, which doesn't have to be a problem, though you have to realize that you mix it, so if you re-use the validator in a per-entity validation scenario, the validator has to know it shouldn't make the entity INvalid if a contained related entity is invalid.

With your single-collection validation requirement, you can use a trick of course: create a single instance of the entityvalidator you want to use and set each entity's EntityValidatorToUse property to that instance, then all entities report errors to the same error collection.

Frans Bouma | Lead developer LLBLGen Pro
bmoeskau
User
Posts: 54
Joined: 15-Jun-2005
# Posted on: 20-Jun-2005 18:02:33   

The EntityCollection class accepts an IValidator to set in each NEW entity instance it creates itself (for example by using a grid) It doesn't have an EntityValidatorToUse property yet, which was an oversight and will be added in 1.0.2005.1, though that's again the validator instance to be SET in NEW entities.

OK, I think this is definitely what I'm looking for -- a way to tell the collection what Entity validator to use for each member of its collection. Now we're getting somewhere simple_smile

There's no collection wide validation mechanism build in, (as there's no cross-entity validation build in), yet.

The main issue with collection wide validation is: you've to propagate the results upwards. So, if you validate the customer.Orders[n].OrderDetails collection, and an OrderDetail is invalid, is customer then also invalid? If so, the validator of Customer has to report invalid, though it can, by performing the validation DOWNwards to orderdetail, if you want to.

I'm not sure about propagating errors upward "out" of child collections. The way I think about it is that each OrderDetail should have an IEntityValidator and the collection itself should have an IEntityCollectionValidator that I can also implement or override myself, although a nice default implementation would obviously be simply reporting the aggregate IsValid state of its children. So the collection can be valid or invalid, but then the parent Order still has its own IEntityValidator in which I can choose whether or not to implement a business rule like "if (myDetails.IsValid) ...". This is nice too because I can code that rule conditionally so that I can make the child collection validity a requirement only sometimes but not others.

So it's basically DOWNward validation like you say, but that seems to make sense to me and be pretty straightforward. But maybe I'm still missing something? wink What do you think?

However, it then gets a bit complicated, because you mix full-graph validation with 'per-entity' validation, which doesn't have to be a problem, though you have to realize that you mix it, so if you re-use the validator in a per-entity validation scenario, the validator has to know it shouldn't make the entity INvalid if a contained related entity is invalid.

I think that's still the same as what I described, and it still seems reasonable to me.

With your single-collection validation requirement, you can use a trick of course: create a single instance of the entityvalidator you want to use and set each entity's EntityValidatorToUse property to that instance, then all entities report errors to the same error collection.

I was actually thinking about resorting to that, but thought I might be missing an easier way. Guess not. wink Thanks, I will try that out.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 21-Jun-2005 11:58:45   

bmoeskau wrote:

I'm not sure about propagating errors upward "out" of child collections. The way I think about it is that each OrderDetail should have an IEntityValidator and the collection itself should have an IEntityCollectionValidator that I can also implement or override myself, although a nice default implementation would obviously be simply reporting the aggregate IsValid state of its children. So the collection can be valid or invalid, but then the parent Order still has its own IEntityValidator in which I can choose whether or not to implement a business rule like "if (myDetails.IsValid) ...". This is nice too because I can code that rule conditionally so that I can make the child collection validity a requirement only sometimes but not others.

So it's basically DOWNward validation like you say, but that seems to make sense to me and be pretty straightforward. But maybe I'm still missing something? wink What do you think?

I think you pretty much summed it up simple_smile , with the exception that you can also define 'validation' points, like 'validate on load', 'validate on addition', 'validate on save' etc. Implementing all that in the runtimes will be hard if you want to please everybody, but at least the hooks should be there for users so they can add small bits of code which then call into their own BL rule engine.

Frans Bouma | Lead developer LLBLGen Pro