function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Jesus GJesus G 

Call a @future method without arguments

Hello,

I am preparing a class that will be automatically executed after having a sandbox refreshed and I wondered how I could call a future method that is part of a different class which already determines the records that need to be updated.
public class AfterSandboxRefreshAuxiliar {
    @future
    public static void updateUsersAfterRefresh () { 
		List <User> listofUsers = new List<User>();
		List <User> usersToUpdate = [SELECT Id,email
        	                      	FROM User
            	               		WHERE Profile.Name = 'System Administrator'];
        For (User u : usersToUpdate) {
            u.email = u.email.replace ('noemail', '');
			listofUsers.add(u);            
        }
        update listofUsers;
	}
}

I cannot use something like the following code so, any ideas, please?
AfterSandboxRefreshAuxiliar.updateUsersAfterRefresh();

Many thanks!
J
Best Answer chosen by Jesus G
Jesus GJesus G
Hello!

I have managed to solve the issue by creating a Set with the ID of the records that I need to update and using it as the argument of the future method that is being called. I guess this is what Amit explained in his post but I could not see it when using the example of the trigger (Many thanks for your help Amit!).

1)
List<User> listUsersToUpdate = [SELECT Id,ProfileId
                                FROM User
                                WHERE Profile.Name = 'System Administrator'];
Set<Id> usersToUpdate = new Set<Id>();
        
	For (User u : listUsersToUpdate) {
		usersToUpdate.add(u.Id);
	}
        
AfterSandboxRefreshAuxiliar.updateUsersAfterRefresh(usersToUpdate);

2)
global class AfterSandboxRefreshAuxiliar {
    @future
    public static void updateUsersAfterRefresh (Set<Id> usersId) {
	
	/* Rest of the code */
	
	}
}

Thank you,
J
 

All Answers

Amit Chaudhary 8Amit Chaudhary 8
Please try to update your class to globel like below. I hope should help you
global class AfterSandboxRefreshAuxiliar {
    @future
    public static void updateUsersAfterRefresh () { 
		List <User> listofUsers = new List<User>();
		List <User> usersToUpdate = [SELECT Id,email
        	                      	FROM User
            	               		WHERE Profile.Name = 'System Administrator'];
        For (User u : usersToUpdate) {
            u.email = u.email.replace ('noemail', '');
			listofUsers.add(u);            
        }
        update listofUsers;
	}
}
NOTE :-

1) Methods with the future annotation must be static methods
2) can only return a void type
3) The specified parameters must be primitive data types, arrays of primitive data types, or collections of primitive data types
4) Methods with the future annotation cannot take sObjects or objects as arguments.
5) You can invoke future methods the same way you invoke any other method. However, a future method can’t invoke another future method
6) No more than 50 method calls per Apex invocation
7) Asynchronous calls, such as @future or executeBatch, called in a startTest, stopTest block, do not count against your limits for the number of queued jobs
8) The maximum number of future method invocations per a 24-hour period is 250,000 or the number of user licenses in your organization multiplied by 200, whichever is greater
9) To test methods defined with the future annotation, call the class containing the method in a startTest(), stopTest() code block. All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously


IMP:-
The reason why sObjects can’t be passed as arguments to future methods is because the sObject might change between the time you call the method and the time it executes. In this case, the future method will get the old sObject values and might overwrite them.  To work with sObjects that already exist in the database, pass the sObject ID instead (or collection of IDs) and use the ID to perform a query for the most up-to-date record. The following example shows how to do so with a list of IDs

Let us know if this will help you
 
Jesus GJesus G
Hello Amit,

Many thanks for your reply, but I still have the same issue: 'Method must define a body'.

My question would be around where and how to define the records that must be updated. I did something similar in the past but using a trigger and the result was something like:
AfterSandboxRefreshAuxiliar.updateUsersAfterRefresh(Trigger.newMap.keySet());
However, the records of my query are defined in the future method now.

Thank you very much,
J
Amit Chaudhary 8Amit Chaudhary 8
hi Jesus G ,

Issue is coming because in your class we are not passing Set<Id> but at the time of calling you are passing set of ID.
If you want to send set<ID> and want to use that one then please update your code like below
 
global class AfterSandboxRefreshAuxiliar {
    @future
    public static void updateUsersAfterRefresh (Set<Id> setId) // use set id in below code
   { 
		List <User> listofUsers = new List<User>();
		List <User> usersToUpdate = [SELECT Id,email
        	                      	FROM User
            	               		WHERE Profile.Name = 'System Administrator'];
        For (User u : usersToUpdate) {
            u.email = u.email.replace ('noemail', '');
			listofUsers.add(u);            
        }
        update listofUsers;
	}
}
Call like below code.
AfterSandboxRefreshAuxiliar.updateUsersAfterRefresh(Trigger.newMap.keySet());




 
Jesus GJesus G
Hello Amit,

Thank you for your reply but please, notice that this code is not being executed as part of a trigger but it is part of a class that implements the SandboxPostCopy Interface, so Trigger.newMap.keySet() is not useful here.

Thank you,
J
Jesus GJesus G
Hello!

I have managed to solve the issue by creating a Set with the ID of the records that I need to update and using it as the argument of the future method that is being called. I guess this is what Amit explained in his post but I could not see it when using the example of the trigger (Many thanks for your help Amit!).

1)
List<User> listUsersToUpdate = [SELECT Id,ProfileId
                                FROM User
                                WHERE Profile.Name = 'System Administrator'];
Set<Id> usersToUpdate = new Set<Id>();
        
	For (User u : listUsersToUpdate) {
		usersToUpdate.add(u.Id);
	}
        
AfterSandboxRefreshAuxiliar.updateUsersAfterRefresh(usersToUpdate);

2)
global class AfterSandboxRefreshAuxiliar {
    @future
    public static void updateUsersAfterRefresh (Set<Id> usersId) {
	
	/* Rest of the code */
	
	}
}

Thank you,
J
 
This was selected as the best answer