Override saving of entity to save other sub entity

Posts   
 
    
Posts: 254
Joined: 16-Nov-2006
# Posted on: 23-Apr-2008 00:36:26   

When the higher level entity is saved e.g. Person which consists of sub entities such as Address I want to override the Save behaviour to add the address entity details in e.g. based at runtime on values of other properties in the Person object. I also only want to do this for new entities.

I was wondering the best place to put this or rather firstly what would actually work simple_smile . I've tried overriding

a) InsertEntity b) OnSave c) Save

The outer top level entity Save method is called with true as the first argument to indicate a recursive save. However whilst the InsertEntity and OnSave methods are called, the IsNew check works and the sub entity can be created, when the outer Save method is called the inner entity is not saved to the data store.

Any thoughts on the best way to do this which will obviously work?

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 23-Apr-2008 11:00:23   

Maybe you can use OnVaildateEntityBeforeSave()

btw, which LLBLGen Pro runtime library version are you using?

Posts: 254
Joined: 16-Nov-2006
# Posted on: 23-Apr-2008 21:39:22   

I'm using the latest 2.5 runtime 2.5.08.0328

I think the issue here might be that LLBLGEN creates a graph of objects when the initial Save(true) of the top level object is called.

Therefore in PersistQueue the graph of objects to save has already been determined, so any of the methods I mentioned above will not have any effect on any new entities which need to be saved as a result of logic in those methods.

e.g. code such as this in PersistQueue

foreach( ActionQueueElement<IEntity> element in queueToPersist ) { ... }

Has already executed at this point

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 24-Apr-2008 05:28:05   

Does this work?:

public override bool Save(IPredicate updateRestriction, bool recurse)
{
     // ... do your custom logic

     base.Save(updateRestriction, recurse);
}
David Elizondo | LLBLGen Support Team
Posts: 254
Joined: 16-Nov-2006
# Posted on: 29-Apr-2008 22:27:46   

No however oddly enough this override isn't called.

However I'm callling a higher level entity Save method with true to indicate a recursive save e.g.

Person p; p.Save(true);

If Person contains an address object which has the overriden Save method, it doesn't appear to be called.

However doing some further investigation it seems the overriden method is only called if you call the Save method directly on the sub entity however that's obviously not ideal as I'd like to only call Save on the top level entity using a recursive save to save any dirty sub entities.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 30-Apr-2008 10:34:40   

I still think you can use validation (OnVaildateEntityBeforeSave ) for the child entity (Address).

Create a validator class to validate the Address Entity and implement the ValidateEntityBeforeSave method, there you could use soemthing like (dummy example):

AddressEntity address = (AddressEntity)involvedEntity;
if(address.Person != null)
{
    address.Country = Person.Country;
}

For more info please check the manual for: Using the generated code -> Validation per field or per entity

Posts: 254
Joined: 16-Nov-2006
# Posted on: 30-Apr-2008 13:26:29   

I haven't tried this approach however doesn't this seem wrong though implementing a validator class just to modify the entity before being saved.

I thought validation classes should not be modifying the entity, and the other extensibility points in the LLBLGEN code was the place to do this i.e. the various before / after save events and overrides?

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 30-Apr-2008 15:50:22   

I thought validation classes should not be modifying the entity,

Why not? Validation can be used for various cases. In validation you can raise an exception or an error in case of missing or invalid data. In other cases (in validation) you may set default values in case of missing data, i.e. fields can be set automatically for the entity to be valid and ready for save.

however doesn't this seem wrong though implementing a validator class just to modify the entity before being saved.

You don't have to implement all the methods, anyway if it doesn't worth to go into trouble of implementing a class, you can just override OnValidateEntityBeforeSave. Please check the manual for Validation logic inside Entity classes, in Using the generated code -> Validation per field or per entity.

Posts: 254
Joined: 16-Nov-2006
# Posted on: 30-Apr-2008 19:59:19   

This seems completely illogical to override validation code to modify an entity before being saved however I did try this i.e. using code such as

protected override void OnValidateEntityBeforeSave()
            {
                base.OnValidateEntityBeforeSave();

                PerformUpdates();
            }

and also changed the order around to

            protected override void OnValidateEntityBeforeSave()
            {
                PerformUpdates();

                base.OnValidateEntityBeforeSave();
            }

and this didn't work.

Frans, please could you provide your thoughts on the most appropriate way to extend the framework generated to do this?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-May-2008 11:43:08   

Validation of address should be done before address is saved. Validation after a save is useless, so it has to be done before it's saved.

What I don't get is what you want to do. It seems you don't want to do any validation, you want to add values to Address when Person is saved, correct? Or do you want to do address validation based on person?

Frans Bouma | Lead developer LLBLGen Pro
Posts: 254
Joined: 16-Nov-2006
# Posted on: 01-May-2008 16:08:30   

Hi Frans

Yes from original post

I want to override the Save behaviour to add the address entity details in e.g. based at runtime on values of other properties in the Person object

I don't want to do any validation at all.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 02-May-2008 09:58:15   

MattAdamson wrote:

Hi Frans

Yes from original post

I want to override the Save behaviour to add the address entity details in e.g. based at runtime on values of other properties in the Person object

I don't want to do any validation at all.

It's not recommended to do things at the last possible point in time you can do things. For example if you want to add values to the AddressEntity instances based on the Person values, you should do that when the AddressEntity is associated to Person. So you should the OnRelatedEntitySet/Unset methods in AddressEntity. Override them in a partial class of AddressEntity and in there check whether the associated entity is person. If so, obtain the values.

If this is too early, i.e. when values are added later to person and which have to end up in 'address', I have a bit of a hard time understanding why you want to do this: shouldn't these values be better located in 'address' then ? simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Posts: 254
Joined: 16-Nov-2006
# Posted on: 02-May-2008 10:11:46   

Hi Frans,

Sorry I actually changed the entities slightly to address and person originally as I thought it would be easier for everyone to understand a concept more familiar.

I actually have say a FileEntity which contains details about a file such as NoLines which I wait to actually retrieve from the local file at save time i.e. in this save override method, change the value of NoLines property from the value taken from business logic which opens the file and finds the no of lines. The actual NoLines property is in a sub property of FileEntity called FileMetricsEntity.

I wanted to add code in this override like

if(entity.IsNew) { // Calculate file metrics and set properties }

Hope this makes sense now.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 02-May-2008 17:43:41   

MattAdamson wrote:

Hi Frans, Sorry I actually changed the entities slightly to address and person originally as I thought it would be easier for everyone to understand a concept more familiar.

We specifically specified in the guidelines of this forum to use the REAL names.

I actually have say a FileEntity which contains details about a file such as NoLines which I wait to actually retrieve from the local file at save time i.e. in this save override method, change the value of NoLines property from the value taken from business logic which opens the file and finds the no of lines. The actual NoLines property is in a sub property of FileEntity called FileMetricsEntity.

I wanted to add code in this override like

if(entity.IsNew) { // Calculate file metrics and set properties }

Hope this makes sense now.

The problem is: when you save FileEntity, and you start creating fileMetricsEntities based on the FileEntity values, it has to be done before the queue is calculated, otherwise the entities aren't persisted obviously. So InsertEntity is way too late.

Save is also not the right candidate as it's not called when you save a collection of these entities.

So, whatever you try to do, you can't do it when the save process has been started. The problem is that you want to calculate the metrics values when the save process is started, but that entity might not exist at that point yet. It also might be that the metrics entity isn't saved as it might not be dirty and the fileentity might be not new.

If you add a new metrics entity to a new file entity (and why not, it should belong to that entity), it will always be saved when that new file entity is saved.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 254
Joined: 16-Nov-2006
# Posted on: 02-May-2008 20:47:42   

Thanks for the explanation, and thought as much from looking at the source code.

If you add a new metrics entity to a new file entity (and why not, it should belong to that entity), it will always be saved when that new file entity is saved.

I agree however I didn't want client code to add the new metrics entity to the file entity before saving, I wanted the business object FileEntity to be responsible for doing this.

I'm a little unclear are you saying this isn't possible to achieve in any of the framework overriden methods / properties / events then? If not then my current solution is to expose the current private UpdateFileMetrics method to be public and ensure that client code calls this method before calling Save.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 03-May-2008 10:59:48   

I hope it's clear that when you save a FileEntity and it doesn't have a FileMetricsEntity yet, it can't be added when the queue is processed: if the queue is updated during the process, it can be the new entities have to be saved first, i.o.w. the sorting has to be redone which is impossible, if you're updating the queue when it's processed. simple_smile

You can create a FileMetricsEntity when you create a FileEntity, e.g. in the OnInitialized method.

Another way to do this is by passing the FileEntity to a BL method which takes care of it before save, e.g. preprocesses it. THere, you can check whether it has a filemetricsentity, and if not, it's added, filled and the fileentity is good to go.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 254
Joined: 16-Nov-2006
# Posted on: 04-May-2008 21:40:49   

I hope it's clear that when you save a FileEntity and it doesn't have a FileMetricsEntity yet, it can't be added when the queue is processed: if the queue is updated during the process, it can be the new entities have to be saved first, i.o.w. the sorting has to be redone which is impossible, if you're updating the queue when it's processed.

That's clear now thanks however perhaps a future version could provide an extension / override in the framework before any save related work in the base classes has started to provide the opportunity for the business object to do work such as this.

Another way to do this is by passing the FileEntity to a BL method which takes care of it before save, e.g. preprocesses it. THere, you can check whether it has a filemetricsentity, and if not, it's added, filled and the fileentity is good to go.

Sure that's certainly another approach however it seems a shame to have to put this in another method just to do this when I'd just like to "Save" the entity and the entity be responsible for this. I'll work with this for now