[SalesForce] Recursive Trigger / Trigger Context Issue

So there are tons of questions on here an d on the boards around using static variables to keep trigger from recursively calling itself. There also is a lot of information out there around best practices in triggers. These bets practices include only having one trigger per object and executing all logic outside of the trigger itself in a helper class. My issue seems to be trying to merge these two best practices. Helper classes for triggers, and use of static variables to prevent recursive trigger calls

Just for simplicity sake, lets assume the logic in the helper classes is ok. It's bulkified, its optimized, etc. Here is my Account Trigger

trigger AccountTrigger on Account (before insert, after insert, before update, after update) {

    AccountTriggerHelper helper = new AccountTriggerHelper();

    if(trigger.isBefore && trigger.isUpdate){
        helper.updateAccountUpdateSummary(trigger.new, trigger.old, trigger.newMap, trigger.oldMap);    
    }   

    if(trigger.isAfter){
        if(trigger.isUpdate){
            helper.maintainCAAccountTeamMembers(trigger.new, trigger.old, trigger.newMap, trigger.oldMap, trigger.isInsert, trigger.isUpdate);
            helper.updateCortextOpportunityOwners(trigger.new, trigger.old, trigger.newMap, trigger.oldMap);
        }
        if(trigger.isInsert){
            helper.maintainCAAccountTeamMembers(trigger.new, trigger.old, trigger.newMap, trigger.oldMap, trigger.isInsert, trigger.isUpdate);
        }
    }
}

As you can see I just have a helper class that I call the methods to execute the logic for the trigger. These all work great if I have them separated (each their own trigger). v The issue is I have test code that when I run is erroring out with a 'Too many SOQL queries' error.

When I check the debug logs, it seems that my triggers are being called recuresively, and it keeps going until it has run enough times in the same context to get the too many SOQL queries error.

So I thought I would use my TriggerContextUtility class to help with this. Its just a class with a static variable to keep track of the first run of the trigger. This works fine in my triggers with only one method being called.

Heres the triggerContetUtility class

public class TriggerContextUtility {

    private static boolean firstRun = true;

    public static boolean isFirstRun() {
        return firstRun;
    }
    public static void setFirstRunFalse(){
        firstRun = false;
    }
}

Very simple. Normally, I would use this utility class like below to ensure that the trigger only fires once per context, but adding it like below doesn't work

trigger AccountTrigger on Account (before insert, after insert, before update, after update) {

    AccountTriggerHelper helper = new AccountTriggerHelper();
    if(TriggerContextUtility.isFirstRun()){
        if(trigger.isBefore && trigger.isUpdate){
            helper.updateAccountUpdateSummary(trigger.new, trigger.old, trigger.newMap, trigger.oldMap);    
        }   

        if(trigger.isAfter){
            if(trigger.isUpdate){
                helper.maintainCAAccountTeamMembers(trigger.new, trigger.old, trigger.newMap, trigger.oldMap, trigger.isInsert, trigger.isUpdate);
                helper.updateCortextOpportunityOwners(trigger.new, trigger.old, trigger.newMap, trigger.oldMap);
            }
            if(trigger.isInsert){
                helper.maintainCAAccountTeamMembers(trigger.new, trigger.old, trigger.newMap, trigger.oldMap, trigger.isInsert, trigger.isUpdate);
            }
        }
    }
    TriggerContextUtility.setFirstRunFalse();
}

This does not work however. It seems my before methods are called, but not my after methods. I have tried moving the TriggerContextUtility.setFirstRunFalse(); into different places, but nothing seems to correctly fire all methods.

So I either leave out my static variable check and the trigger is recursively calling itself until it errors out. OR, I fix this recursive call using my TriggerContextUtility class, but then not all methods within the trigger are being called.

Can anyone offer any insight as to what I am doing wrong here? Am I missing something. This seems like something that should be simple, but for some reason I can't get it to work. Hoping for another set of eyes to find the flaw in my logic here.

EDIT

Here is the debug statements I added to the trigger

trigger AccountUpdatesTrigger on Account (before insert, after insert, before update, after update) {

    AccountTriggerHelper helper = new AccountTriggerHelper();
    if(TriggerContextUtility.isFirstRun()){
        if(trigger.isBefore && trigger.isUpdate){
            helper.updateAccountUpdateSummary(trigger.new, trigger.old, trigger.newMap, trigger.oldMap);    
        }   
        *system.debug('First Run Value Line 8: ' + TriggerContextUtility.isFirstRun());*

        if(trigger.isAfter){
            if(trigger.isUpdate){
                *system.debug('First Run Value Line 11: ' + TriggerContextUtility.isFirstRun());*
                helper.maintainCAAccountTeamMembers(trigger.new, trigger.old, trigger.newMap, trigger.oldMap, trigger.isInsert, trigger.isUpdate);
                helper.updateCortextOpportunityOwners(trigger.new, trigger.old, trigger.newMap, trigger.oldMap);
                *system.debug('First Run Value Line 14: ' + TriggerContextUtility.isFirstRun());*
            }
            if(trigger.isInsert){
                *system.debug('First Run Value Line 17: ' + TriggerContextUtility.isFirstRun());*

                helper.maintainCAAccountTeamMembers(trigger.new, trigger.old, trigger.newMap, trigger.oldMap, trigger.isInsert, trigger.isUpdate);
            }
            TriggerContextUtility.setFirstRunFalse();
        }
    }   
}

The line 14 debug statement is outputting FALSE. Why would that be?

Best Answer

I don't think you want the ContentUtility check to be at the Trigger level - I typically refer to it in my helper class, or even at the individual method level in that class. There may well be instances where you want one method to run twice if something was updated, but not others. So I would move the check for TriggerContextUtility into the helper class and I think it will work fine...

As an update - I have always used these posts as my guide.

http://gokubi.com/archives/two-interesting-ways-to-architect-apex-triggers http://www.embracingthecloud.com/2010/07/08/ASimpleTriggerTemplateForSalesforce.aspx

There are (many!) more variations, but I would keep simple at this stage... – BritishBoyinDC 1 min ago edit

Related Topic