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
VictorBVVictorBV 

MIXED_DML_OPERATION in a future method - How is it possible?

Hi,

 

I am provisioning Customer Portal via Apex so I need to create users dinamically.

After some reading and developing I faced this problem that puzzles me.

I got next error executing testing

 

TestUserCreationForContact.singleInserTest277.0System.DmlException: Insert failed. First exception on row 0; first error: MIXED_DML_OPERATION, La operación DML en el objeto de configuración no está permitida después de actualizar un objeto no configurado (o viceversa): User objeto original: Account: []Class.Utilities.createNominalUser: line 53, column 3 External entry point

 

I show you the method createNominalUser

 

 

  @future  public static void createNominalUser(String alias, String email, String emailEncodingKey, String lastName, String languageLocaleKey, String localesKey, 
ID profile, String timeZonesIdKey, String userName, ID userRole)
{
Database.DMLOptions dmo = new Database.DMLOptions();
dmo.EmailHeader.triggerUserEmail = true;
User user = new User(alias = alias, email = email, emailencodingkey = emailEncodingKey, lastname = lastName, languagelocalekey = languageLocaleKey,
localesidkey = localesKey, profileid = profile, timezonesidkey = timeZonesIdKey, username = userName, userRoleId = userRole);

user.setOptions(dmo);
insert user; //line 53
  System.debug('Usuario nominal creado, Id:' + user.Id);
}

 How is that possible?

How can I fix it?

 

 

Best Answer chosen by Admin (Salesforce Developers) 
VictorBVVictorBV

Ok.

 

I used some strange trick I found... and I dont know why neither how but it worked.

 

I just used System.runAs to wrap Test, like this

 

 

System.runAs(new User(Id = System.UserInfo.getUserId()))
{
       Test.startTest();
       insert userDirector;
       insert userSupervisor;
       Test.stopTest();        
}

 

And it worked.

Someone can explain why that worked to solve the problem?

 

Thanks very much

 

All Answers

SurekaSureka

Hi,

 

Are you trying to insert/update/delete some other object's record in the same @future method?

 

Because in an apex class/method, where you are performing DML operation in the User, you can't perform DML operation on other objects.

 

If so, try removing the corresponding objects DML statement.

 

Thanks

VictorBVVictorBV

Hi Sureka,

 

Thanks for your quick answer.

As you can see, I only insert a user in my @future method.

 

I post the method which calls the creation, maybe there is something wrong with it

 

 

trigger UserCreationForContact on Contact (after insert) {
if(Trigger.isAfter)
{
if(Trigger.isInsert)
{
//Obtenemos los roles que tienen los contactos a insertar
List<String> rolesId = new List<String>();
//Obtenemos las cuentas que tienen los contactos
List<ID> accountsId = new List<ID>();
//Idiomas que usan los contactos
List<ID> languagesId = new List<ID>();
for(Contact contact: Trigger.new)
{
System.debug('En bucle - RoleId: ' + contact.RoleCode__c + ' - AccountId: ' + contact.AccountId);
rolesId.add(contact.RoleCode__c);
accountsId.add(contact.AccountId);
languagesId .add(contact.LanguageCode__c);
}//Fin for(Contact c: Trigger.new)

//leemos el campo SecurityAcces de los Role__c
Map<String, String> securityAccess = new Map<String, String>();
List<Role__c> listRoles = [SELECT Id, SecurityAccess__c FROM Role__c WHERE Id IN :rolesId];
for(Role__c role : listRoles)
{
System.debug('En bucle - RoleId: ' + role.Id + ' - SecurityAccess__c: ' + role.SecurityAccess__c);
securityAccess.put(role.Id, role.SecurityAccess__c);
}
Map<Id, Account> accountMap = new Map<Id, Account>([SELECT ID, Name FROM Account WHERE Id IN :accountsId]);
Map<Id, Language__c> languageMap = new Map<Id, Language__c>([SELECT Id, LanguageCode__c FROM Language__c WHERE Id IN :languagesId]);

System.debug('Contactos a insertar:' + Trigger.new.size());
System.debug('Numero de cuentas asociadas a los contactos a insertar: ' + accountMap.size());
System.debug('Numero de roles asociados a los contactos a insertar: ' + listRoles.size());
System.debug('Numero de lenguajes asociados a los contactos a insertar: ' + languageMap.size());



Profile portalProfile = [select id, Name from profile where name='Customer Portal Manager Custom'];

Profile directorProfile = [select id from profile where name= :Utilities.directorProfile];
UserRole directorRole = [select id from UserRole where name= :Utilities.directorRole];

for(Contact contact: Trigger.new)
{
try
{
Account a = accountMap.get(contact.AccountId);
String companyName = '';
if(a == null)
{
companyName = Utilities.mainCompanyName;
}
else
{
companyName = a.Name;
}
String email = '';
String userName = '';
if(contact.Email == null || contact.Email == '')
{
email = Utilities.emailGenerico;
userName = contact.UserCode__c + '@' + Utilities.webDomain;
}
else
{
email = contact.Email;
userName = contact.Email;
}

String sA = securityAccess.get(contact.RoleCode__c);
System.debug('Id del contacto: ' + contact.Id + ' - Nombre del Contacto: '+ contact.Name +' - SecurityAccess del rol asociado: ' + sA);

if(sA == 'SN' || sA == 'SY')
{
//Se crea usuario de portal
/*UserRole role = [select id, Name from UserRole where name = :companyName + ' Cliente Usuario'];
System.debug('Role para la creacion del usuario de portal - Name: ' + role.Name + ' Id: ' + role.Id);*/
System.debug('Perfil para la creacion del usuario de portal - Name: ' + portalProfile.Name + ' Id: ' + portalProfile.Id);
Utilities.createPortalUser(contact.UserCode__c, email, Utilities.emailEncoding, contact.LastName,
Utilities.translateLanguageCode(languageMap.get(contact.LanguageCode__c).languageCode__c),
Utilities.localesidkey, portalProfile.Id, Utilities.translateTimeZone(''), userName, contact.Id);

}//Fin if(sA == 'SN' || sA == 'SY')
if(sA == 'DN' || sA == 'DY')
{
Utilities.createNominalUser(contact.UserCode__c, email, Utilities.emailEncoding, contact.LastName,
Utilities.translateLanguageCode(languageMap.get(contact.LanguageCode__c).languageCode__c),
Utilities.localesidkey, directorProfile.Id, Utilities.translateTimeZone(''), userName,
directorRole.Id);
}//Fin if(sA == 'DN' || sA == 'DY')
}
catch (Exception ex)
{
System.debug('Error en la creacion del usuario al insertar un contacto con Id = ' + contact.Id + ' y Nombre: ' + contact.LastName +
' Mensaje de la excepcion: ' + ex.getMessage());
throw new TriggerException('Error en la creacion del usuario al insertar un contacto con Id = ' + contact.Id + ' y Nombre: ' + contact.LastName, ex);
}
}//Fin for(Contact contact: Trigger.new)
}//Fin if(Trigger.isInsert)
}//Fin if(Trigger.isAfter)
}

 

Thanks in advance

 

VictorBVVictorBV

Ok.

 

I used some strange trick I found... and I dont know why neither how but it worked.

 

I just used System.runAs to wrap Test, like this

 

 

System.runAs(new User(Id = System.UserInfo.getUserId()))
{
       Test.startTest();
       insert userDirector;
       insert userSupervisor;
       Test.stopTest();        
}

 

And it worked.

Someone can explain why that worked to solve the problem?

 

Thanks very much

 

This was selected as the best answer
rfanrfan

See

 

http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_dml_non_mix_sobjects.htm?SearchType=Stem

 

Specifically "The primary exception to this is when you are using the runAs

method in a test. For more information, see System Methods"

 

Thanks

Ryne