4

I've got a class with an @future annotated method that gets called in an after Trigger context variable (it's an asynchronous callout, so it needs to be in Trigger.after) and performs Database.update(objectList, false) DML.

Currently it takes in some parameters, plus Set<Id> which gets passed in via Trigger.newMap.keySet(). I can query for the records in the method itself, process them and update them.

This works well for records passed in via Trigger.insert context, but if I try this in Trigger.update, I'm going to run into recursion problems. To do this I'm going to have to compare fields and if they're different, update them. Otherwise don't.

Now if I pass in Trigger.newMap().keySet() and Trigger.oldMap.keySet() and query for them, I'm going to get the same records. As per the docs, I can't pass in a List<sObject> nor a Map<Id, sObject> so I'm stuck in a position where I need to compare old and new sObjects, but can only pass in the Id.

Below is some code that roughly represents what I'm looking to do:

if (Trigger.isAfter) {
    if (Trigger.isInsert) {
        MyClass.MyMethod(Trigger.newMap.keySet()); // Works fine. Can query for the records
    }
    if (Trigger.isUpdate) {
        MyClass.MyMethod(Trigger.new, Trigger.old); // Won't work. Salesforce doesn't allow sObjects passed in to @future methods
        MyClass.MyMethod(Trigger.newMap, Trigger.oldMap); // Won't work. Salesforce doesn't allow Maps passed into @future methods
        MyClass.MyMethod(Trigger.newMap.keySet(), Trigger.oldMap.keySet()); // Won't work. Querying for the Ids will return the same values as it's in the after context
    }
}

public class MyClass {
    @future
    public static void MyMethod(Set<Id> mySet) {
        List<sObject> objList = Database.query('SELECT [...] FROM [...] WHERE Id IN :mySet');

        // Change field values

        Database.update(objList, false);
    }

    @future
    public static void MyMethod(Map<Id, sObject> newMap, Map<Id, sObject> oldMap) {
        List<sObject> objList = new List<sObject>();

        for (sObject obj : newMap.values()) {
            sObject oldObj = oldMap.get(obj.Id);

            if (obj.Field__c != oldObj.Field__c) {
                objList.add(obj);
            }
        }

        if (objList.size() > 0) {
            // Update stuff...

            Database.update(objList, false);
        }
    }
}

Now I know that I could just simply do something similar to the second method above and compare the values and call the method in the Trigger itself but the idea behind what I'm writing is to make the class itself do the legwork and keep Trigger code minimal.

So I'm wondering if anyone has get any luck in doing something creative and similar to what I'm after, or I'll have to resign myself to doing the comparison logic in the Trigger.

Is it possible to compare Trigger.new/newMap with Trigger.old/oldMap in an @future method?

4
  • 7
    Serialize/deserialize from/to JSON?
    – kurunve
    Commented Feb 22, 2017 at 10:25
  • As per my understanding, you want to avoid recursion when the records are updated from your future method.If that is the case, you can encapsulate your trigger in If(!System.isFuture()) which will always return false if the trigger is getting called because of the updation done in future method
    – Samir
    Commented Feb 22, 2017 at 10:34
  • @kurunve Thanks, that may actually be one viable solution.
    – Dan Jones
    Commented Feb 22, 2017 at 10:41
  • @Samir Avoiding recursion isn't the problem, comparing old and new values in an @future method is really what I'm trying to do.
    – Dan Jones
    Commented Feb 22, 2017 at 10:41

2 Answers 2

12

You can user JSON.serialize() in your trigger to serialize the Old and New Maps and JSON.deserialize() in your @future annotated method to get the actual object and compare the old and new values.

So, your trigger code would be something like

if (Trigger.isUpdate) {
    String jsonOldMap = JSON.serialize(Trigger.oldMap);
    String jsonNewMap = JSON.serialize(Trigger.newMap);

    MyClass.MyMethod(jsonOldMap, jsonNewMap); 
}

and your @future annotated method code would be something like

@future
public static void MyMethod(String stroldMap, String strnewMap) {
    
    map<id, sobject> oldMap = (Map<id, sObject>) JSON.deserialize(stroldMap, map<id, sobject>.class);
    map<id, sobject> newMap = (Map<id, sObject>) JSON.deserialize(strnewMap, map<id, sobject>.class);
    
    for (sObject obj : newMap.values()) {
        sObject oldObj = oldMap.get(obj.Id);

        if (obj.Field__c != oldObj.Field__c) {
            objList.add(obj);
        }
    }

    if (objList.size() > 0) {
        // Update stuff...

        Database.update(objList, false);
    }
}

A pretty good detailed article can be viewed here.

2

Thats a great post @Mahmood. I would like to correct your example of deserializing just to get the correct sense out of it.

map<id, sobject> oldMap = (Map<id, sObject>) JSON.deserialize(stroldMap, map<id, sobject>.class);
map<id, sobject> newMap = (Map<id, sObject>) JSON.deserialize(strnewMap, map<id, sobject>.class);
1
  • Thanks, @Sam - updated my answer.
    – Mahmood
    Commented May 16, 2023 at 15:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .