The correct place for custom entity logic on saves and deletes

Posts   
 
    
mdbruning
User
Posts: 18
Joined: 10-Mar-2008
# Posted on: 06-Jul-2016 08:32:40   

Hello all,

We are currently doing a complete rewrite of the backend of our platform, including a switch from SelfServicing to Adapter. What we did in the past (as also mentioned in the post below) is using the validator classes to execute custom logic when an entity is saved and/or deleted.

Otis replied in the post below that this is not the proper place as these classes are meant for validation and not for fetching other data. I therefore assume that setting other properties on the validated entity is also not the intention.

My big question then is what the best place is to put this custom logic? I read about the OnSave (SelfServicing) and the OnBeforeEntitySave (Adapter), which definitely could work for us. However, in the validator classes we also have a AfterSave and a BeforeDelete. Are these also possible?

And is there also an AfterDelete?

Thanks in advance, Mathieu

Here's the link to the old post: https://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=23679)

mdbruning
User
Posts: 18
Joined: 10-Mar-2008
# Posted on: 06-Jul-2016 16:30:03   

Hello again,

I sat down in the sun today and gave it some thought and I think I have a decent solution. Here it goes:

As I read in the post below, the best way to implement custom logic when an entity is saved and/or deleted is by overriding the OnSaveEntity and OnDeleteEntity in the data access adapter. However, that does not give me the opportunity directly to add custom logic on a per entity basis.

What I came up with to overcome this is the following: I add an interface to the entity template, which defines a property CustomLogicExecutor (this is not the actual name yet, but I stil have to think of a better one). The type of CustomLogicExecutor is a class with overridable methods OnSave, OnSaveComplete, OnDelete, OnDeleteComplete. I also add the CustomLogicExecutor property to the entity template, so this property is generated for each entity.

In my Injectables assembly, I add CustomLogicExecutor classes for each of the entities I want to execute custom logic on. Using LLBLGen's dependency injection functionality, I inject the CustomLogicExecutor classes on the newly generated property on the entities.

In the data access adapter class I will override the OnSaveEntity, OnSaveEntityComplete, OnDeleteEntity and OnDeleteEntityComplete. In these overrides, I will cast the entity to my interface, check whether the CustomLogicExecutor is set and if so, call the appropriate method on that one.

Hope this gets Otis' approval wink and perhaps helps anybody else as well.

Cheers.

This is the post with someone describing the same issue: https://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=1691&HighLight=1

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 06-Jul-2016 16:37:37   

You could implement the interface on a partial class of CommonEntityBase I think simple_smile It's the base class for all entities. No need to generate anything simple_smile For the rest I think you're good to go simple_smile

Frans Bouma | Lead developer LLBLGen Pro
mdbruning
User
Posts: 18
Joined: 10-Mar-2008
# Posted on: 06-Jul-2016 16:41:30   

Ah yes, didn't think of that one, thanks Frans!

Would it be an idea to add these classes in a future version of LLBLGen by default? In the same way that the validator property exists on entity classes by default?

I can imagine that more people would like to do similar things.

Cheers.

mdbruning
User
Posts: 18
Joined: 10-Mar-2008
# Posted on: 06-Jul-2016 20:20:42   

Hi Frans,

I just read this post (https://llblgen.com/tinyforum/Messages.aspx?ThreadID=560&StartAtMessage=0&#2578) which says that I can't change any field values on the passed entity parameter in the OnSaveEntity method as the query to save the entity was already created.

That changes a lot for me as I often want to set certain field values within these custom logic classes.

I can of course override the SaveEntity method on the DataAccessAdapter class instead of the OnSaveEntity, although I'm not sure how to do the SaveEntityComplete then.

What your take on this?

Cheers, Mathieu

mdbruning
User
Posts: 18
Joined: 10-Mar-2008
# Posted on: 06-Jul-2016 20:30:55   

Is something like this going to work?


        public override bool SaveEntity(IEntity2 entityToSave, bool refetchAfterSave, IPredicateExpression updateRestriction, bool recurse)
        {
            bool success;

            // Call the custom OnSave logic
            ICustomLogicExecutor executor = entityToSave as ICustomLogicExecutor;
            if (executor != null && executor.Executor != null)
            {
                executor.Executor.OnSaveEntity(entityToSave);
            } 

            // Save the entity
            success = base.SaveEntity(entityToSave, refetchAfterSave, updateRestriction, recurse);

            // Call the custom OnSaveComplete logic
            if (executor != null && executor.Executor != null)
            {
                executor.Executor.OnSaveEntityComplete(entityToSave);
            }

            return success;
        }

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 07-Jul-2016 11:35:42   

mdbruning wrote:

Ah yes, didn't think of that one, thanks Frans!

Would it be an idea to add these classes in a future version of LLBLGen by default? In the same way that the validator property exists on entity classes by default?

I can imagine that more people would like to do similar things.

No we don't want more of this in the framework: we think it's better to offer the tools to add it manually rather than forcing users to follow along a path which might not be as flexible as they want.

mdbruning wrote:

I just read this post (https://llblgen.com/tinyforum/Messages.aspx?ThreadID=560&StartAtMessage=0&#2578) which says that I can't change any field values on the passed entity parameter in the OnSaveEntity method as the query to save the entity was already created.

True, that's why you use OnBeforeEntitySave() for that simple_smile

Be aware that the entities to persist are already selected. So if you want to alter values in an entity which is currently not dirty, that's not going to be possible: the entity already has to be selected as being persisted. OnBeforeEntitySave is there so you can alter whatever you want, before the query is created.

Frans Bouma | Lead developer LLBLGen Pro
mdbruning
User
Posts: 18
Joined: 10-Mar-2008
# Posted on: 07-Jul-2016 12:00:37   

I get the OnBeforeEntitySave(), although I also need the OnAfterEntitySave, OnBeforeEntityDelete and OnAfterEntityDelete. However, those do not seem to be there simple_smile

Is my approach with overriding the DataAccessAdapter's SaveEntity and DeleteEntity method (as per code example above) then possible?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 07-Jul-2016 12:55:39   

mdbruning wrote:

I get the OnBeforeEntitySave(), although I also need the OnAfterEntitySave, OnBeforeEntityDelete and OnAfterEntityDelete. However, those do not seem to be there simple_smile

Is my approach with overriding the DataAccessAdapter's SaveEntity and DeleteEntity method (as per code example above) then possible?

OnBeforeEntitySave is for altering values, OnSave and OnSaveComplete is for tapping into when what is done (so you can do other things according to that event). OnDelete / OnDeleteComplete are the ones for entity deletes. They're not meant to alter values, but rather to notify you prior/after a delete action.

It depends on what you're going to do in these events: if you want to deny the delete action for example, you can better use authorization.

What are the use case for pre/post save and pre/post delete actions, what is it that you're going to do there exactly? Perhaps we can then better advice what to do simple_smile

Frans Bouma | Lead developer LLBLGen Pro
mdbruning
User
Posts: 18
Joined: 10-Mar-2008
# Posted on: 08-Jul-2016 12:23:08   

Here are a few example of the use-cases:

Pre save

  • Validating whether certain values have been specified
  • Validating whether certain combinations of values are allowed
  • Setting entity field values
  • Checking whether certain items already exists in a table

Post save

  • Updating timestamps
  • Invalidating a cache
  • Creating related entities that should be there by default

Pre delete

  • Checking whether an item can be deleted

Post delete

  • Updating timestamps
  • Invalidating a cache

We already use authorization for determining which user role can which operations on the database.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 08-Jul-2016 13:45:51   

Pre save •Validating whether certain values have been specified (Use the Validation framework) •Validating whether certain combinations of values are allowed (Use the Validation framework) •Setting entity field values (if you want to initialize field values then override OnInitializing or OnInitialized, if you want to set values before persisting the entity then override OnBeforeEntitySave in a partial class of the specific entity, or of the CommonEntityBase, if the logic is common for all or some entities) •Checking whether certain items already exists in a table (It's not clear what are you checking, is it about concurrency? are you checking if an entity with the same PK exists? or an entity with similar data? or checking about some other data in another table? In most of the cases (except for concurrency checks, I believe this falls into the application business logic, and maybe a Fetch is needed to check on what you want, before you attempt the save)

Post save •Updating timestamps (Isn't this automatically maintained by the database (timestamp/rowversion), or are you speaking about something else?, please clarify.) •Invalidating a cache (OnSaveComplete) •Creating related entities that should be there by default (I'd consider this a pre-save action, where you use OnBeforeEntitySave to add those related entities to the entity being saved, and make sure the save is recursive)

Pre delete •Checking whether an item can be deleted. (How would the actual check be upon?, is this a check on dependent related entities existence which are on the FK side. This is very tricky, and the question comes down to, do you want the entity marked "undeletable" so the user wouldn't be able to delete it, in this case no Pre-Delete event would be available, coz there is no delete has been attempted. If you want the user to attempt to delete the entity, then you want to visit the database to check on certain conditions if not valid, you'd go back to notify the user that the delete can not be performed, otherwise the delete would be executed, in this case wouldn't a direct execute of the delete be more efficient to save the first database hit, and if it's not deleted you'd get an exception that you can handle and notify the user accordingly.)

Post delete •Updating timestamps (Not sure which timestamps to update, please clarify) •Invalidating a cache (Seems to me a business logic action, that better be handled on higher layers of the application).