- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Recursive Entity Validation Woes
We're trying to ensure recursive saves call appopriate entity validators for related entities. For example, Customer-> Orders. How do we ensure that when a recursive save occurrs , the customer and all related orders have an the appropriate entity validators set.
I have three scenarios I can think of to help solve this:
-
Create an entity factory class that is responsible for creating the derived classes and setting the entity validators. all clients will be required to go through the factory when needing concrete objects.
-
In the SaveEntity() method in the controllers, check if the save is recursive. if so, we are assuming which entities up front need to be saved recursively, so we will set the entity validators on the objects determined ahead of time. otherwise, we will have to figure out the entire object graph and set all the objects (yikes!)
-
Override OnSaveEntity() in the DataAccessAdapter and raise a custom event, which the controller can listen for. When the controller receives this, it can figure out which entity validator it needs to set and call the Validate() method.
Does anyone have any ideas on which one sounds most logical?
Thanks All
2 and 3 won't do. 2 is insane , you shouldn't do that. and 3 is too late, the query is already made.
If you're using the extended entity templates for adapter, you can add the validator object creation in the constructor. This way you'll be safe and always have the right validator.
Very soon you'll be able to add this code to the native generated code in a special region which is preserved between code generation cycles so you don't need any derived entities just to add this.
[quotenick="Otis"]2 and 3 won't do. 2 is insane , you shouldn't do that. and 3 is too late, the query is already made.
quote]
What if we are not changing any data, all we want to do is inject the EntityValidator and call Validate()?
Otis wrote:
If you're using the extended entity templates for adapter, you can add the validator object creation in the constructor. This way you'll be safe and always have the right validator.
Very soon you'll be able to add this code to the native generated code in a special region which is preserved between code generation cycles so you don't need any derived entities just to add this.
I would love to use this approach, however, some of our core business logic needs to make database calls. With this in the entity project, we can't do that (Controller Scenario).
Can you think of any other ways to ensure you can fire off your business logic, regardless of whether or not any database calls need made, when a save or save(recursive) is made?
MarcoP wrote:
Otis wrote:
2 and 3 won't do. 2 is insane
, you shouldn't do that. and 3 is too late, the query is already made.
What if we are not changing any data, all we want to do is inject the EntityValidator and call Validate()?
It's not going to work, the graph walk traversal routine is complex and not open, you have to re-implement it, which is IMHO not that useful, as you don't have to. Just override SaveEntity() and everything an entity's save cycle is about to start, you'll get a call, even before the entity's Validate() method is called.
Validate() is already called first thing an entity enters SaveEntity(). So if you just want to inject the entityvalidator, you can do that in an override of SaveEntity() -> add the validator and call base.SaveEntity()..
Otis wrote:
If you're using the extended entity templates for adapter, you can add the validator object creation in the constructor. This way you'll be safe and always have the right validator.
Very soon you'll be able to add this code to the native generated code in a special region which is preserved between code generation cycles so you don't need any derived entities just to add this.
I would love to use this approach, however, some of our core business logic needs to make database calls. With this in the entity project, we can't do that (Controller Scenario).
true, so SaveEntity() can be the right spot. You can reference the entity project from the database specific project, that won't create a cycle in the references.
Can you think of any other ways to ensure you can fire off your business logic, regardless of whether or not any database calls need made, when a save or save(recursive) is made?
SaveEntity() is the best spot, as with that, you won't miss any update, only the UpdateEntitiesDirectly(). You can switch that one off by overriding that routine as well and throw an exception there.
Joined: 26-Oct-2003
MarcoP wrote:
Can you think of any other ways to ensure you can fire off your business logic, regardless of whether or not any database calls need made, when a save or save(recursive) is made?
How about listening for IDataAccessAdapter.FetchEntity. You can associate the necessary validators there.
Jeff...
MarcoP wrote:
Save I fetch a customer and prefetch the associated addresses. Do you know if that will fire off that event for each entity?
No. FetchEntity() is not used in prefetch paths.
I don't think FetchEntity() is a good place to add generic objects to an entity, as the entity can be fetched using a lot of methods, and also you can instantiate the entity just like CustomerEntity c = new CustomerEntity(); ...
Joined: 26-Oct-2003
Otis wrote:
MarcoP wrote:
Save I fetch a customer and prefetch the associated addresses. Do you know if that will fire off that event for each entity?
No. FetchEntity() is not used in prefetch paths.
I don't think FetchEntity() is a good place to add generic objects to an entity, as the entity can be fetched using a lot of methods, and also you can instantiate the entity just like CustomerEntity c = new CustomerEntity(); ...
Hmmm...looks like we're back to a custom GeneralEntityFactory, which isn't a bad solution. Why isn't FetchEntity called in prefetch paths?
Jeff...
jeffreygg wrote:
Otis wrote:
MarcoP wrote:
Save I fetch a customer and prefetch the associated addresses. Do you know if that will fire off that event for each entity?
No. FetchEntity() is not used in prefetch paths.
I don't think FetchEntity() is a good place to add generic objects to an entity, as the entity can be fetched using a lot of methods, and also you can instantiate the entity just like CustomerEntity c = new CustomerEntity(); ...
Hmmm...looks like we're back to a custom GeneralEntityFactory, which isn't a bad solution. Why isn't FetchEntity called in prefetch paths? Jeff...
Well, if I pull 2 customer entities and they both have 100 orders, I want to read 200 orders in 1 go, not 200 times call FetchEntity . This is because the nodes in a path are fetched as a collection, so it is faster.
FetchEntity is also not the only entity fetch routine: you also have FetchUsingUniqueConstraint and FetchNewEntity(). They all call internally FetchEntityUsingFilter, but that routine is not public.
jeffreygg wrote:
Hmmm...looks like we're back to a custom GeneralEntityFactory, which isn't a bad solution. Why isn't FetchEntity called in prefetch paths? Jeff...
I really like the GenericEntityFactory() solution, however, what happens when entities are fetched?
These entities will not have their EntityValidators set. I would think this would be a common problem for anyone implementing their own controller, manager classes.
Seems that recursive saves may not be the way to go. Maybe there should just be overloaded version of SaveEntity() in the business layer, where each version would take in different related enttities. For example:
public void SaveEntity(CustomerEntity c) {..}
public void SaveEntity(CustomerEntity c, AddressEntity[] a) {..}
public void SaveEntity(CustomerEntity c, AddressEntity[] a, ContactEntity[] cs) {..}
This would cause more code to be written in this way, but we now have more control flow. now each method would be responsible for creating their own transaction and passing the DataAccessAdapter to each of the other controllers to use. Does this sound bad?
I am a little hesitant about overriding SaveEntity() in the adapter and putting any validation in here. I am really trying to stick all business rules/validation in my controller classes.
I'm experiencing the same issues and I'd love a good solution. It seems the main problem is getting the validators set in the place. Maybe the validators classes that are generated now could be extended to implement a default IEntityValidator that's automatically associated with the entity?
Joined: 24-Feb-2005
Cadmium wrote:
I'm experiencing the same issues and I'd love a good solution. It seems the main problem is getting the validators set in the place. Maybe the validators classes that are generated now could be extended to implement a default IEntityValidator that's automatically associated with the entity?
Interesting...
I would love to see the designer allow the developer to browse to an existing assembly, and select a type that implements some pre-defined interface, that will be consulted before any saves take place. This would help insure the necessary business logic is always "plugged-in" and executed, therefore these type of issues wont exist.
Just a thought...
Cadmium wrote:
I'm experiencing the same issues and I'd love a good solution. It seems the main problem is getting the validators set in the place. Maybe the validators classes that are generated now could be extended to implement a default IEntityValidator that's automatically associated with the entity?
I know this is a problem. I've addressed this in two ways, in the upgrade which goes beta next week.
1) via an include template which is included into the initialization method of an entity class. This way you can inject code into the initialization code at generation time, so be sure that by that you set the entityvalidator object always 2) via regions in the initialization code to which you can add your own code which is then preserved between code generation cycles.
1) is sufficient if you want to add the same validator to a lot of entities. 2) is sufficient if you want to add specific validators to specific entities. Either way I think it is the best solution. All others are flaky in one way or the other.
Joined: 24-Feb-2005
I am not yet familiar with the templates technology, so excuse me if I am off base here:
Otis wrote:
1) via an include template which is included into the initialization method of an entity class. This way you can inject code into the initialization code at generation time, so be sure that by that you set the entityvalidator object always
Let's say I would like to inject code that may need to interact with other entities and the database. Can this be injected in the code?
Otis wrote:
2) via regions in the initialization code to which you can add your own code which is then preserved between code generation cycles.
This would be a great place for adding required fields logic!
Kristian wrote:
I am not yet familiar with the templates technology, so excuse me if I am off base here:
Otis wrote:
1) via an include template which is included into the initialization method of an entity class. This way you can inject code into the initialization code at generation time, so be sure that by that you set the entityvalidator object always
Let's say I would like to inject code that may need to interact with other entities and the database. Can this be injected in the code?
The initialization routine for an entity is not the right place for that.
Otis wrote:
2) via regions in the initialization code to which you can add your own code which is then preserved between code generation cycles.
This would be a great place for adding required fields logic!
![]()
I'm not sure if I understand this correctly Could you elaborate on this a bit, please, so I can understand what you mean and perhaps tweak what I've setup now?
Joined: 24-Feb-2005
Otis wrote:
Kristian wrote:
I am not yet familiar with the templates technology, so excuse me if I am off base here:
Otis wrote:
1) via an include template which is included into the initialization method of an entity class. This way you can inject code into the initialization code at generation time, so be sure that by that you set the entityvalidator object always
Let's say I would like to inject code that may need to interact with other entities and the database. Can this be injected in the code?
The initialization routine for an entity is not the right place for that.
Otis wrote:
2) via regions in the initialization code to which you can add your own code which is then preserved between code generation cycles.
This would be a great place for adding required fields logic!
![]()
I'm not sure if I understand this correctly
Could you elaborate on this a bit, please, so I can understand what you mean and perhaps tweak what I've setup now?
I thought I read a post a few days ago where you said you were going to create a region that would be called whenever a save takes place on an entity. I could very easily have been mistaken!
So, is this new feature your adding meant for the developer to create EntityValidators classes in the data project and then you create these in the initialization region?
Kristian wrote:
Otis wrote:
Kristian wrote:
I am not yet familiar with the templates technology, so excuse me if I am off base here:
Otis wrote:
1) via an include template which is included into the initialization method of an entity class. This way you can inject code into the initialization code at generation time, so be sure that by that you set the entityvalidator object always
Let's say I would like to inject code that may need to interact with other entities and the database. Can this be injected in the code?
The initialization routine for an entity is not the right place for that.
Otis wrote:
2) via regions in the initialization code to which you can add your own code which is then preserved between code generation cycles.
This would be a great place for adding required fields logic!
![]()
I'm not sure if I understand this correctly
Could you elaborate on this a bit, please, so I can understand what you mean and perhaps tweak what I've setup now?
I thought I read a post a few days ago where you said you were going to create a region that would be called whenever a save takes place on an entity. I could very easily have been mistaken!
![]()
No, you're correct.
In adapter this is already the case: override SaveEntity().
In SelfServicing I've now added OnSave and OnSaveComplete methods, which are called before and right after the save. You can do extra things in there if you'd like by overriding them in the custom code region in the entity.
So, is this new feature your adding meant for the developer to create EntityValidators classes in the data project and then you create these in the initialization region?
Yes. You define IEntityValidator classes, and in the initialization routine you create a new instance of such a class and add it to the entity. Then you are sure the entity has the validator object.
Joined: 24-Feb-2005
Very cool!
So, I am assuming the BaseClass has a virtual method defined, and when we override SaveEntity(), we will call Entity.SomeVirtualMethod().
As for some of the people asking above how to ensure their business rules are always injected before a save takes place (access database or other controllers), I would create a class the implements IEntityValidator, stick it in your business layer, and use reflection to set the EntityValidatorToUse property in the custom code region. How does this sound.
Joined: 07-Feb-2005
What I did to solve this problem is adjust the selfservice templates a bit to include a standard entity validator which I bind to the entity in its constructors. Just as in the standard template one validator is defined per entity, the difference is mine also implement the IEntityValidator interface. Here is an example of one of the constructors of the AdresEntity:
public AdresEntity():base(new PropertyDescriptorFactory(), new AdresEntityFactory())
{
base.EntityValidatorToUse = (IEntityValidator)base.Validator;
}
To have the validators implement the IEntityValidator interface I also had to change those:
public class AdresValidator : Validator, IValidator, IEntityValidator
{
public AdresValidator()
{
this.entityToValidate = null;
}
public AdresValidator(AdresEntity entityToValidate)
{
this.entityToValidate = entityToValidate;
}
protected void ValidateEntity(AdresEntity entity, bool recurse)
{
//Validate entity and fill brokenRules collection with errors
}
public virtual bool Validate(int fieldIndex, object value)
{
// TODO: Add AdresValidator.Validate implementation
return true;
}
public override void Validate()
{
if (entityToValidate == null)
throw new ApplicationException("No entity specified to validate");
brokenRules.Clear();
ValidateEntity((AdresEntity)entityToValidate, true);
}
#region IEntityValidator Members
bool SD.LLBLGen.Pro.ORMSupportClasses.IEntityValidator.Validate(object containingEntity)
{
brokenRules.Clear();
ValidateEntity((AdresEntity)containingEntity, false);
if (brokenRules.Count > 0)
throw new ValidatorException("Validate fails, see brokenRules", this);
return true;
}
#endregion
#region Custom User Code
#endregion
}
The IEntityValidator.Validate function uses the function ValidateEntity to do the actual validation. This function fills a collection with error strings giving details as to what is wrong with the entity.
I can also call this function without going through an entity save because the validators all derive from the base class Validator. This way I can also instantiate a validator to validate entire entities including the entities they reference. I use this to give the user feedback as to what is wrong with the entity he (or she) is currently editing.
The only problem I am having right now is that if I validate an entity recursively and somewhere along this recursion the same entity gets referenced it loops in validation.... Right now I have solved this by stopping recursion in certain entities, another solution would be to keep track of the entities that are being validated but that comes down (in a way) to what is mentioned the above messages as option 2... Or simple cut off recursion at a pre-specified depth. Any thoughts on this?
btw. I must admit llblgen is one of the best products (if not THE best) I bought in years, it has already saved one of my projects! Thanks!!!
Kristian wrote:
Very cool! So, I am assuming the BaseClass has a virtual method defined, and when we override SaveEntity(), we will call Entity.SomeVirtualMethod().
No, there are two situations: adapter and selfservicing.
In adapter you have: SaveEntity() and OnSaveEntity(). If you override SaveEntity(), and SaveEntity is called for a given entity, you immediately get the call in your derived method. If you override OnSaveEntity() you get the call right before the actual query is executed.
In SelfServicing this mechanism wasn't that available. What I've done now is that when the entity's save logic is about to be invoked, I call OnSave(). If this method is overriden, you get teh call in your overriden method. You then can do last minute things and throw an exception if it has to be aborted for example.
Ben wrote:
The IEntityValidator.Validate function uses the function ValidateEntity to do the actual validation. This function fills a collection with error strings giving details as to what is wrong with the entity.
I can also call this function without going through an entity save because the validators all derive from the base class Validator. This way I can also instantiate a validator to validate entire entities including the entities they reference. I use this to give the user feedback as to what is wrong with the entity he (or she) is currently editing.
Clever!
The only thing I see is that you merged field validation with entity validation, which can be ok, but they're called on different levels, which might become a problem later on.
The only problem I am having right now is that if I validate an entity recursively and somewhere along this recursion the same entity gets referenced it loops in validation.... Right now I have solved this by stopping recursion in certain entities, another solution would be to keep track of the entities that are being validated but that comes down (in a way) to what is mentioned the above messages as option 2... Or simple cut off recursion at a pre-specified depth. Any thoughts on this?
It loops because you call from your own validator the validator of another entity and that one calls again yours etc? If you just validate the entity the entityvalidator is assigned to, that wouldn't cause recursion I think.
btw. I must admit llblgen is one of the best products (if not THE best) I bought in years, it has already saved one of my projects! Thanks!!!
You're more than welcome!