3

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?

  • 7
    Serialize/deserialize from/to JSON? – kurunve Feb 22 '17 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 Feb 22 '17 at 10:34
  • @kurunve Thanks, that may actually be one viable solution. – Dan Jones Feb 22 '17 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 Feb 22 '17 at 10:41
9

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, sobject);
    map<id, sobject> newMap = (Map<id, sObject>) JSON.deserialize(strnewMap, 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);
    }
}

A pretty good detailed article can be viewed here.

| improve this answer | |
0

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);
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.