+ Start a Discussion
TraceyTracey 

MIXED_DML_OPERATION, DML operation on setup object is not permitted Error

I'm trying to copy a new user as contact by using a trigger but I'm getting the following error

 

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

 

trigger Acceleration_User_Copyto_Contact on User (after insert, after update) {
    
   // Map<String,User> newLead = new Map <String, User>();
    
    system.debug('Acceleration_User_Copyto_Contact Trigger....');
 
    for ( User user : System.Trigger.new)
    {
        if  (user.Email.contains('acceleration'))
        {
            system.debug('Acceleration_User_Copyto_Contact Trigger..2.');
            Integer RecordCount = [select count() from Contact c where c.Email = : user.Email];
            
            system.debug('Acceleration_User_Copyto_Contact Trigger..2a .' + RecordCount);
            
            if (RecordCount == 0)
            {
                String AccountId = '';
                for ( Account a : [Select a.Id From Account a where Name = 'Acceleration']) 
                {
                    system.debug('Acceleration_User_Copyto_Contact Trigger..3.');
                     
                    AccountId = a.Id;
                
                    Contact con = New Contact();
                    con.AccountId = AccountId ;
                    con.Email = user.Email;
                    con.Firstname = User.Firstname;
                    con.Lastname = User.Lastname ;
                    con.User__c = User.Id;
                    insert con;
                }          
            }                   
        }
        
     }
    
    
}

 

Best Answer chosen by Admin (Salesforce Developers) 
wesnoltewesnolte

Hey

 

There you cannot perform DML on what salesforce calls setup objects(User in this case) and non-setup object in the same context. There is a workaround that can be found here although I still had some issues. Let me know if the posted solution works for you, if not mail me and I'll send you mine.

 

Cheers,

Wes 

All Answers

wesnoltewesnolte

Hey

 

There you cannot perform DML on what salesforce calls setup objects(User in this case) and non-setup object in the same context. There is a workaround that can be found here although I still had some issues. Let me know if the posted solution works for you, if not mail me and I'll send you mine.

 

Cheers,

Wes 

This was selected as the best answer
trigon-grouptrigon-group

I have just run into the same issue when attempting to update the User object, as well as a custom object in the same Apex class.  Can you please educate me how you got around this.  I am not understanding the other contributor's workaround.

 

Thanks you!

 

osamanosaman

There is a work around for that.

If you want to perform two DML operations sequentially, create a seprate method for 2nd DML operation with @future token.

make sure you put System.RunAs() in the method. Your overall method should look like this

 

private static void myFunc()

{

     ///1st DML operation

 

     User usr = [Select id from User where Id = :UserInfo.getUserId()];

 

     System.RunAs(usr)

     {

        Test.startTest();

         myFunc2();

         Test.stopTest();

     }

 

 

}

 

@future

private static void myFunc2()

{

   ///2nd DML operation

}

OyeCodeOyeCode

Can you please send me the solution to pandeyhm@gmail.com. I have been dealing with the same issue

john hejohn he

is it resolved?

mr.marksmr.marks

i dont get what this bug means

im not performing two dml operations sequentially

and it only pops up when i try to set the userRoleId of a test user

 

this is happening in my test class where i simulate a manager approving something 

im creating an account and a case and having defualt user call up a page

i then create a test user and try to call up the same page

but when i set the users role id, i get this error

if i dont set the role, no error

 

not sure if bug...

mr.marksmr.marks

heres some code for y'all

error happens on the 'insert u;' line

 

/**
Code to test Case Signature Extension Class
*/
@isTest
public class CaseSignatureExtensionTestClass{

    static testMethod void testAuthorButtons(){
        system.debug('starting case signature test');
        
        
        // setup account
        RecordType rt = [Select Id,SobjectType,Name From RecordType WHERE Name = 'Healthcare Account' and SobjectType = 'Account' limit 1];
        Account account = new Account(Name = 'Test account', RecordTypeId = rt.Id, Industry='Other', ShippingCountry='USA');
        insert account;
        // create case
        rt = [Select Id,SobjectType,Name From RecordType WHERE Name = 'Tech Support' and SobjectType = 'Case' limit 1];
        Case test_case = new Case(OwnerId = UserInfo.getUserId(), accountId = account.id, RecordTypeId = rt.Id, Component__c = 'Workstation/PC' );
        insert test_case;
        
        //System.runAs(u){
            PageReference sigPageRef = Page.CaseSignatures;
            Test.setCurrentPage(sigPageRef);
        
            ApexPages.StandardController controller = new ApexPages.StandardController(test_case);
            CaseSignatureExtension controllerExt = new CaseSignatureExtension(controller);
        //}
        controllerExt.submitSig();
        
        // create test user
        Profile p = [SELECT Id FROM Profile WHERE Name='Standard User']; 
        User u = new User(Alias = 'standt', Email='standarduser@testorg.com', 
                EmailEncodingKey='UTF-8', isActive = False, LastName='Testing', LanguageLocaleKey='en_US', 
                LocaleSidKey='en_US', ProfileId = p.Id, UserRoleId = '00E30000000jF9G',
                TimeZoneSidKey='America/Los_Angeles', UserName='test@testorg.com');
        insert u;
        
        System.runAs(u){
            PageReference mgrPageRef = Page.CaseSignatures;
            Test.setCurrentPage(mgrPageRef);
        
            ApexPages.StandardController mgrPgeController = new ApexPages.StandardController(test_case);
            CaseSignatureExtension mgrControllerExt = new CaseSignatureExtension(mgrPgeController);
            
            mgrControllerExt.rejectSig();
        }
        
        //
        
    }
}

 

sornasorna

I think this issue is resolved now. I am creating a user from a contact trigger and after that I am making an update to the contact. and it seems to be working fine. It seems mixed dml is allowed now. not sure when this change was made - may bespring 13?

kmillerrrkmillerrr

I'm still getting this error with Summer '13.  My test class creates a user, then creates an account.  Strangely, the test runs just fine in the Force.com IDE but gets the MIXED_DML_OPERATION error in the standard UI.  As far as I can tell I'm using best practices for unit testing - creating all my test data on the fly, including a user that my test needs to be there.  So why is this happening, and what is the current best workaround?  

 

I've never encountered this before, so it seems like maybe the bug had been fixed but was re-introduced in Summer '13.

Kirill_YunussovKirill_Yunussov

I am gettiing this error in a trigger that updates a user and then attempts to update a custom object.   I'll be using the @future to get around it, but it's not beautiful because @future methods only take primitives, and i need to update a list of objects.   ehhhhh...

Ashish_ShelarAshish_Shelar

Hi all ,

 

its not fixed in any of the Salesforce release .The thing is that ,there is concept called as setup and non-setup objects e.d USer and contact .

You cant perform the two DML operations on these objects in the same context .Only workaround is using @future method in another class. But remember there is batch class running on any of the setup and non setup objects ,the batch class will fails .

Only workaround is use of @future method .

 

Regards,

Ashish

 

R DraperR Draper
I know this is an old post but I have a better solution than @future.  Use System.runAs(someOtherUser){insert setup objects}  You are allowed to have more than one runAs block in your test code and this puts it in a different context.  That way you won't run into issues with waiting for the Future method to insert your setup objects. Enjoy!
karol.freebergkarol.freeberg
I am having the exact same issue doing the same task. Does anyone have code out there that acutally does this task successfully? 
adityaMorganadityaMorgan
make your method as @future...
Jack D PondJack D Pond
The suggested @future works for adding associated custom non-setup objects in the same context with setup objects(User in this case).  It took a while to figure out.  Hope these snippets help.

For the previously created custom non-setup object User_Access_Privileges__c that links back to User using the lookup field Related_User__c (use Create->Object->New to create your own):

The integration class containing the @future method looks like this (create using Develop->Apex Classes->New)
public class UserAccessAddKludge {
    public UserAccessAddKludge(Id uid){
        List<Id> UserID = new List<Id> {uid};
        AddUserAccessObject(UserID);
    }
    @future
    private static void AddUserAccessObject(List<Id> uid){
        User u = [ Select id from User where Id = :uid[0] ];
        User_Access_Privileges__c UA = new User_Access_Privileges__c(Related_User__c = u.ID);
        insert UA;
    }
}
It is referenced from the following User Trigger UserAccessAdd as follows:
Note:  TeamTrackingLogUpdates is a shared class which allows batch jobs to turn off this trigger because this trigger will cause issues if run in batch mode.
 
trigger UserAccessAdd on User (after insert,after update) {
    if (TeamTrackingLogUpdates.notinbatch){
        for ( User u : Trigger.New ) {
            if (Trigger.isInsert) {
                UserAccessAddKludge uak = new UserAccessAddKludge(u.Id);
            }
            else if (Trigger.isAfter) {
                System.Debug('After: ' + u.LastName + ' ID: ' +  u.Id);
                User_Access_Privileges__c UA;

// If related User_Access_Privileges__c does not exist for this User
                try {
                    UA = [Select Id from User_Access_Privileges__c where Related_User__c = :u.ID ];
                }
                catch (Exception e) {
                    UserAccessAddKludge uak = new UserAccessAddKludge(u.Id);
               }
            }
        }
    }
}
And here's the TeamTrackingLogUpdates shared class just for good measure:
// @file
// @author Jack D. Pond <jack.pond@dc.gov>
// @ingroup Trigger/Batch Management
// @license GNU General Public License 2.0 or later
// @Description Static Class to keep triggers from executing multiple times, creating
//              redundant entries.
//              
//              By adding a shared class, variables can be shared across processes.
//              Used here to disable triggers for batch processing and testing
//              
// 
public with sharing class TeamTrackingLogUpdates {
    public static boolean notinbatch = true;
}

Testing Hint:  Use    Test.startTest() before adding/updating the setup object and Test.stopTest() after before your system.Assert tests to ensure the @future has executed before testing.




 
Robert LawRobert Law
I'm having this issue when I am trying to provision a user and contact using SAML2 SSO.  Does anyone have a solution to this?
Abhisek BhattaAbhisek Bhatta
@Robert
Instead of Insert or update call use Database.Insert or Database.update inside try-catch and that should solve your problem.
Priyadarshini Manoharan 3Priyadarshini Manoharan 3
I had the same error, in the test class for the trigger SyncProductstoUser. I fixed it without @futurecall as per R Draper
System.DmlException: Update failed. First exception on row 0 with id 00318000008sC90AAE; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, SyncProductstoUser: execution of AfterUpdate

caused by: System.DmlException: Update failed. First exception on row 0 with id 00518000000MvpVAAS; first 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: Account: []

Trigger.SyncProductstoUser: line 65, column 1: []

Working Solution
Trigger:
trigger SyncProductstoUser on Contact (after update) 
{

    Set<Id> UserIds = new Set<Id>();
    Set<Id> ConIds = new Set<Id>();

    for (Contact contactInLoop : Trigger.new)
    {
        if(contactInLoop.Id != null && contactInLoop.Id != null)
        {    
            ConIds.add(contactInLoop.Id);    

        }
    }
    
   
        List<User> Users = [Select Id,Product__c, Product_Access__c, ContactId from User  where ContactId =: ConIds];
   
    

        Map<Id, User> UserContactMap = new Map<Id, User>();
        
        // loop through all retrieved Contacts 
        for(User u : Users)
        {
        // get all Contacts for the User
           UserContactMap.put(u.ContactId, u); // put the User Contacts in a map by Contact Id
      
        }
    



        for (Contact contactInLoop : Trigger.new)
        {
            if (contactInLoop.Id != null  && contactInLoop.Product_Access__c != null && contactInLoop.Product__c != null )
            {
                User ur = UserContactMap.get(contactInLoop.Id);
                
                if(ur != null)
                {              
                   ur.Product_Access__c = contactInLoop.Product_Access__c;
                   ur.Product__c = contactInLoop.Product__c;
                   update ur;            
                 
                }
                 
            }
        }
    
 
}

Test Class

@isTest(SeeAllData = True)

public class TestSyncProductstoUser
{
    static testMethod void UpdateContact()
    {
        Account Acc = new Account(Name='ABC CORPORATION');
        insert Acc;
        Contact Con = new Contact(AccountId=Acc.Id,FirstName='ABC',LastName='User1',Email='ABC1User1@ABC.com',Product__c='ABC', Product_Access__c='ABC User');
        insert Con;
         
         
       User userToCreate = new User();
       String ProfileId=[Select Id,Name from Profile where Name='ABC Community'].Id;
       
         
       userToCreate.FirstName = Con.FirstName;
       userToCreate.LastName  = Con.LastName;
       userToCreate.Email     = Con.Email;
       userToCreate.Username  = 'ABC@community.com';
       userToCreate.Alias     = 'ABC1';
       userToCreate.ProfileId = ProfileId;
       userToCreate.ContactId = Con.Id;
       userToCreate.Product__c = Con.Product__c;
       userToCreate.Product_Access__c = Con.Product_Access__c;     
       userToCreate.TimeZoneSidKey    = 'America/Denver';
       userToCreate.LocaleSidKey      = 'en_US';
       userToCreate.EmailEncodingKey  = 'UTF-8';
       userToCreate.LanguageLocaleKey = 'en_US';
       
       insert userToCreate;
       
       system.debug('***Contact ID' + Con.Id);
         
       System.runAs(userToCreate)
       {
           Con.Product__c = 'XYZ';
           Con.Product_Access__c = 'XYZ User';
           update Con;
       }
 
    }

}






 
Dennis WilsonDennis Wilson
Long running thread, but recently encounted the MIXED_DML_OPERATION error on some test classes that required setup of User and Group records.

The solution that worked for me was to do the creation of those User/Group test data records using System.runAs.
HariPHariP
The link on the Best answer no longer working, its redirecting back to success home page. Can you please update link to point to updated URL?
 
Mendy - KurantMendy - Kurant
You need to use future if you want to update the user and contact at the same time.
I had the same problem, and this is the solution:


    public void afterUpdate(map<id, User> oldUsers, List<User> newUsers){
        list<id> activeContactIds = new list<id>();
        list<id> unactiveContactIds = new list<id>();
        for (User u : newUsers){
            if (u.isActive && !oldUsers.get(u.id).isActive)
                activeContactIds.add(u.ContactId);
            if (!u.isActive && oldUsers.get(u.id).isActive)
                unactiveContactIds.add(u.ContactId);
        }
        UpdateActiveContacts(activeContactIds, true);
        UpdateActiveContacts(unactiveContactIds, false);
    }
    
    @future
    public static void UpdateActiveContacts(list<id> contactIds, boolean active){
        list<Contact> contacts = new list<Contact>();
        for (id i : contactIds){
            Contact con = new Contact(id = i, Portal_User__c = active);
            contacts.add(con);
        }
        if (contacts.size() > 0)
            update contacts;
    }
Jamie BrowningJamie Browning

(This does not answer the question but asserts that the issue is back).

 

This was previously (maybe last year) not an issue.

We had implemented the below commented code with no issue.

The commented code was working just fine until a recent release (this year).

A new deployment to Prod has uncovered this is now broken again  (In Winter 17) 

So it looks like Salesforce has regressed this issue back into prod.

NB: our issue appeared in a Test Method

 

@isTest static void positiveTest1() {
        User notU = [ Select Id From User 
            Where Id != :UserInfo.getUserId()
            And IsActive = true
            AND UserRole.Name = 'Sales Person'
            Limit 1
        ];

        //User notU = [ Select Id From User 
        //    Where Id != :UserInfo.getUserId()
        //    And IsActive = true
        //    Limit 1
        //];

        //UserRole uR = TestObjectFactory.getUserRole('SalesPerson');
        //notU.UserRoleId = uR.Id;
        //update notU;
Erin YamaokaErin Yamaoka
I'm facing this issue with Process Buider in Spring '17 when trying to create a custom object record as a result of the creation of a user record. Anyone have a work around I can do in Process Builder?
Rakesh Kumar 196Rakesh Kumar 196
I am facing the same issue. I have created a process builder and wants to create a record on a custom object while a new user is getting created and getting the same issue
Ankit Maini.Ankit Maini.
Facing similar type of problem i.e.
That I need to create a User record on insert of custom object,as well as if a error occurs in creation of User than that error should also update in custom object.
What we are doing is in csutom object after insert tirgger we are creating User in future method.If the User is created it is good but if not we need to capture that error and show it in our custom object.
At this point when we need to show the error,this exception is thrown

"MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa)"
Jamie BrowningJamie Browning

@Ankit Maini.
Try a little bit of this:
https://salesforce.stackexchange.com/questions/75779/future-method-calling-another-future-method.
Obvious issue you have is you cannot chain future methods together.
If you can stack your two future methods in order - You can then put the Custom Id into the User Record. Then in the second Future Method you can query to see if the User Record was created (users.size() >0 ) If not update the custom record.
However this will need them to be processed in order and I do not think @Future alone will do this.
Problem with this will be that you may not be able to post the exception message back into the custom object.

Try this
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_queueing_jobs.htm


Lastly, a Salesforce to Salesforce Integration with a tool like Hubofy.com
Realtime callout to Hubofy and then Hubofy manages the rest of the process for you, does 1 or 2 API calls back into your Salesforce.

 

Ankit Maini.Ankit Maini.
@Jamie Browning

apex_queueing_jobs worked for me :)

Thanks
J. PostonJ. Poston
Queueing jobs fixed my issue as well. I actually made a nested class implementing Queueable and created a List as a class variable. Pass the list into the nested class constructor and do the database insert on that.

Thanks @Jamie Browning
Jason Kuzmak 12Jason Kuzmak 12

Thanks @R Draper!

Literally queried myself as a user record in my @testSetup, "ran as" myself, and it let me insert my holiday :)

No @future needed

Johny SinsJohny Sins
Nice information, Thanks for the answering. I am Johny Sins from Plumbers In Mississippi (https://answers.microsoft.com/en-us/profile/4da444fe-01f8-4091-acde-bd266d56eee3) services. 
Tension EndsTension Ends
Superb answer bro thanks for your support .......Follow my Hindi Shayari site Tensionends (http://www.tensionends.com/facebook-status-in-hindi-and-english/)
zabee saqibizabee saqibi