I am trying to upsert a dynamic list of objects into Salesforce using Apex, before point of upsert I have casted a new list to explicitly describe which object this is but I still receive the following error:

DML on generic List only allowed for insert, update or delete


    String objectName = 'Account'; // this will be dyanmic

    // parse lines
    String csvToParse = attachment.Body.toString();

    List<sObject> dynList = CSVReader.readCSVFile(csvToParse, objectName);

    List<SObject> toUpsert = (List<SObject>)Type.ForName('List<' + objectName  + '>').newInstance();
    toUpsert = dynList;

    // upsert records
    SObjectField extField = Account.Fields.ExtID__c;
    Database.UpsertResult[] up = Database.upsert(toUpsert, extField);

You're performing a cast on a value that you then throw away.

List<SObject> toUpsert = (List<SObject>)Type.ForName('List<' + objectName  + '>').newInstance();
toUpsert = dynList;

When you reassign toUpsert to dynList, the fact that the previous value of toUpsert had a runtime type of List<someSobjectOrOther> becomes irrelevant. It has no effect on either the compile-time type of toUpsert, which is List<sObject>, or the compile-time type of dynList, which is also List<sObject>, or the runtime type of dynList, which appears to also be List<sObject> although we cannot see how you're generating that value.

Compile-time types cannot be set dynamically. Runtime types are attributes of values, not of variables. You need to change the runtime type of the actual data here, the value you're passing to Database.upsert().

As an example, this code works fine:

List<SObject> toUpsert;
String objectName = 'Account';

toUpsert = (List<SObject>)Type.ForName('List<' + objectName  + '>').newInstance();
sObject a = (sObject)Type.forName(objectName).newInstance();
a.put('Name', 'Test');

Database.upsert(toUpsert, Account.Id);

What matters to upsert here is that the underlying values (the Account and the List<Account>) have concrete runtime types, even though both values have generic compile-time types. Note that you can reproduce the error you're seeing simply by substituting in

toUpsert = (List<SObject>)Type.ForName('List<sObject>').newInstance();
| improve this answer | |
  • I'm a little confused as to why any of that is even necessary. I've created a List<SObject> and added 5 different SObjects into the list and performed an insert/update/delete (can't upsert but that's easy enough to work around) on it with no issue. What's the value of casting a dynamic list into an SObject list? – gNerb Aug 15 '18 at 14:30
  • 1
    It looks to me like the original poster is doing some kind of data load activity to bring in multiple different objects from CSV data. I see the use case, I'd just rather handle it off-platform myself. – David Reed Aug 15 '18 at 14:54

Not the answer you're looking for? Browse other questions tagged or ask your own question.