[SalesForce] How to advoid mixed dml operation creating user for community

I am trying to create a list of contacts and add these to the community as users. I keep getting the error:

MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Contact: []

I have walked trough some similar questions but could not apply these to my case. Could someone please explain.

Apex

public without sharing class addController {
    @AuraEnabled

    public static void saveContacts(List<Contact> listContact){
        Insert listContact;
        system.debug('listContact '+ listContact);

        User currentUser = [SELECT Id, Contact.AccountId FROM User WHERE Id =: UserInfo.getUserId() LIMIT 1];
        system.debug('currentUser '+ currentUser); 

        List<User> uList= new List<User>(); 
        List<Relatie__c> rel= new List<Relatie__c>(); 
        UserRole ur = [SELECT Id FROM UserRole Where name = 'Partner'];

        Profile profileId = [SELECT Id FROM Profile WHERE Name = 'Customer Community User' LIMIT 1];

        for(Contact con : listContact){

            Relatie__c rc = new Relatie__c(Account__c = currentUser.contact.AccountId, Contactpersoon__c = con.id ) ;
            system.debug('rc '+ rc);
            rel.add(rc); 
            string alias = con.firstName.substring(0,1) + con.lastName.substring(0,1);
            user u = New user(userName = con.firstName+'.'+con.lastName+'@test.nl' ,
                              firstName = con.firstName, 
                              lastName = con.lastName, 
                              alias = alias, 
                              email = con.Email, 
                              communityNickName = alias, 
                              timeZoneSidKey = 'Europe/Amsterdam', 
                              LocaleSidKey = 'nl_NL', 
                              EmailEncodingKey = 'ISO-8859-1', 
                              LanguageLocaleKey = 'nl_NL', 
                              ContactID = con.id,
                              ProfileId = profileId.id,
                              UserRoleId = ur.id
                             );

            uList.add(u); }
        insert rel;
        system.debug('rel '+ rel);
        insert uList;

    }
}

Best Answer

You need to move the User creation to a @future method. See also:

An example of how it might work would be something like:

@future
public static void createUsers(Set<Id> contactIds)
{
    List<User> users = new List<User>();
    Profile profileToAssign = [SELECT Id FROM Profile WHERE ...];
    for (Contact record : [
        SELECT FirstName, LastName FROM Contact WHERE Id IN :contactIds
    ]){
        users.add(new User(
            ContactId = record.Id,
            FirstName = record.FirstName,
            LastName = record.LastName,
            ProfileId = profileToAssign.Id
            // and so on
        ));
    }
    insert users;
}

Then in your synchronous method, you remove all User logic and replace with:

Set<Id> contactIds = new Map<Id, Contact>(listContact).keySet();
createUsers(contactIds);
Related Topic