4

I have some abstracted code, which at the end of some process will create a homogeneous list of SObjects.

It could be a list of Accounts, it could be a list of contacts, it could be a list of some custom SObject. It doesn't matter, every record in the list will be an instance of the same type of SObject.

upsert resultSObjectList

I can't simply do an because it results in:

System.TypeException: DML on generic List only allowed for insert, update or delete

If I send the list back to whatever method called my abstraction and then try to cast it back into whatever type it should be, e.g.:

List<Account> accountList = (List<Account>) resultSObjectList;
upsert accountList;

It still results in:

System.TypeException: DML on generic List only allowed for insert, update or delete

I can make this work with:

List<Account> accountList = new List<Account>();
for (SObject sObj : resultSObjectList) {
    accountList.add((Account) sObj);
}
upsert accountList;

But that is ugly and I will need to make a similar implementation for every consumer of my process.

Likewise, I'd prefer not to create two separate lists (for insert and for update) and then do two separate DML operations.

Any ideas?

3

You need the actual object to be a concrete sobject list type. You can do it either by Type.forName or by String.

SObject[] results = doUpsert(source, List<Account>.class);

...

public SObject[] doUpsert(Sobject[] source, Type listType) {
  SObject[] results = (SObject[])listType.newInstance();
  results.addAll(source);
  upsert results;
  return results;
}

Sobject[] results = doUpsert(source, 'List<Account>');

...

public SObject[] doUpsert(Sobject[] source, String listType) {
  SObject[] results = (SObject[])Type.forName(listType).newInstance();
  results.addAll(source);
  upsert results;
  return results;
}

Either way, these are pretty efficient ways to get things done.


Edit: Based on comments, the following should also be acceptable:

public SObject[] doUpsert(Sobject[] source) {
  if(source != null && source.size() > 0) {
    String listType = 'List<'+source[0].getSObjectType()+'>';
    SObject[] results = (SObject[])Type.forName(listType).newInstance();
    results.addAll(source);
    upsert results;
    return results;
  }
  return null;
}
| improve this answer | |
3

I know this is bit ugly, but serializing and deserializing converts sObject to the actual type they are.

List<Account> accountList = (List<Account>) JSON.deserialize(JSON.serialize(resultSObjectList),List<Account>.class);
upsert accountList;

The above code works.

| improve this answer | |
  • 3
    It works, but incredibly CPU hungry. – sfdcfox Apr 20 '18 at 8:53

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