MIXED_DML_OPERATION error in Salesforce Apex Trigger when updating User objects

Vladimir 'lenin_ra' Halme picture Vladimir 'lenin_ra' Halme · Apr 13, 2012 · Viewed 7.9k times · Source

I have a trigger on a Contact object and when I try to update a User record in this trigger I get the following exception:

"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"

Trigger code:

trigger UpdateContactTrigger on Contact (after update) {

    User u = [SELECT Id, IsActive FROM User WHERE IsActive = true];

    u.IsActive = false;
    update u;

}

How can I avoid this error while updating User record's fields from a Contact trigger?

Answer

Vladimir 'lenin_ra' Halme picture Vladimir 'lenin_ra' Halme · Apr 13, 2012

Salesforce categorizes objects into so called setup and non-setup objects. User is a setup object while Contact is a non-setup object. Salesforce restricts DML operations so that both kinds of objects can't be manipulated in the same context.

There's a workaround for this problem placing DML code for conflicting object to a @future method as described in the end of this document and the previous answer.

In my case using @future method didn't work because there was an update trigger on User that called another @future method and Salesforce doesn't allow calling a @future method from another @future method.

So I came up with another workaround that works for some cases for User objects.

From API version 15.0 Salesforce actually allows updates on custom fields of a User object in the same context with non-setup object updates. So if you need to update User's standard field, you can use a custom proxy field with a 'before update' trigger on the User object.

If you need to change User's IsActive field, add a custom IsActiveProxy field to the user and perform your update in a trigger on it:

trigger UpdateContactTrigger on Contact (after update) {

    User u = [SELECT Id, IsActive FROM User WHERE IsActive = true];

    u.IsActiveProxy__c = false;
    update u;

}

Then create a 'before update' trigger on a User that copies the proxy field value to the standard field:

trigger BeforeUpdateUserTrigger on User (before update) {

    for(User user : trigger.new) {

        if(user.IsActive != user.IsActiveProxy__c) {
            user.IsActive = user.IsActiveProxy__c;
        }

    }

}

That's it! It worked for me.