0

I am trying to upsert a dynamic sObject list of records using Database.Upsert, I want to be able to use AllOrNone parameter however this returns an error saying that my toUpsert variable must be a concrete sObject.

This is my code which brings up that concrete error:

    List<sObject> toUpsert = (List<sObject>)Type.ForName('List<' + objectName  + '>').newInstance();
    for(sObject dynObject : lstObjects) {
        toUpsert.add(dynObject);
    }
    system.debug(toUpsert);
    Database.UpsertResult[] up = Database.upsert(toUpsert, f, false);

The problem is as this needs to be dynamic I'm stuck how this can be dynamic enough to do upsert the records, I've figured using switch statements would be the half way solution here but it compalins that the upsert variable is not available on my Database.upsert line.

Code for half way solution:

        Schema.SObjectField f = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap().get(extKey);

    switch on objectName {
        when 'Account' {
            List<Account> toUpsert = (List<Account>)Type.ForName('List<' + objectName  + '>').newInstance();
            for(sObject dynObject : (List<Account>)lstObjects) {
                toUpsert.add(dynObject);
            }
        }
        when 'Address__c' {
            List<Address__c> toUpsert = (List<Address__c>)Type.ForName('List<' + objectName  + '>').newInstance();
            for(sObject dynObject : (List<Address__c>)lstObjects) {
                toUpsert.add(dynObject);
            }

        }
    }

    Database.UpsertResult[] up = Database.upsert(toUpsert, f, false);
    system.debug(toUpsert);
3

Just put the upsert inside your when blocks.

List<Database.UpsertResult> results;
switch on objectName {
    when 'Account' {
        List<Account> toUpsert = new List<Account>();
        toUpsert.addAll(dynamicRecords);
        results = Database.upsert(toUpsert, externalIdField, /*allOrNone*/false);
    }
    when 'Address__c' {
        List<Address__c> toUpsert = new List<Address__c>();
        toUpsert.addAll(dynamicRecords);
        results = Database.upsert(toUpsert, externalIdField, /*allOrNone*/false);

    }
}
system.debug(results);

If casting works, you can even one line these blocks.

List<Database.UpsertResult> results;
switch on objectName {
    when 'Account' {
        results = Database.upsert((List<Account>)toUpsert, externalIdField, /*allOrNone*/false);
    }
    when 'Address__c' {
        results = Database.upsert((List<Address__c>)toUpsert, externalIdField, /*allOrNone*/false);

    }
}
system.debug(results);
|improve this answer|||||
  • I think I'll still encounter one more issue with this, I wont be able to call toUpsert after the switch case. But I still need to do processing of the failed records by comparing the toUpsert to results. – Deployment Failure Sep 19 '18 at 21:08
  • You can reference your original list (which I renamed to dynamic records. It has the same records in the same order. – Adrian Larson Sep 19 '18 at 21:34
3

Why not declare a generic SobjectList outside the switch case statement?

List<Sobject> listSobject = new List<Sobject>();
switch on objectName {
    when 'Account' {

        for(Account dynObject : (List<Account>)lstObjects) {
            listSobject.add(dynObject);
        }
    }
    when 'Contact' {

        for(Contact dynObject : (List<Contact>)lstObjects) {
            listSobject.add(dynObject);
        }

    }
}

if(listSobject instanceOf List<Account>){
    upsert (List<Account>)listSobject;
}else if(listSobject instanceOf List<Contact>){
    upsert (List<Contact>)listSobject;
}
system.debug(listSobject);
|improve this answer|||||
  • I need to use this within a Database.Upsert which must have a concrete sObject defined, using the code above will not work. – Deployment Failure Sep 19 '18 at 18:02
  • @DeploymentFailure why not use instanceOf keyword to see if its list of account or Contact. Btw it looks like xyproblem.info . Can you edit the question what you are trying to achieve? – Pranay Jaiswal Sep 19 '18 at 18:09
  • I have updated my question. – Deployment Failure Sep 19 '18 at 18:24
  • I need to use Database.Upsert so that I can look at the results to determine if there are any failures, if I were to swap your upsert line with Database.upsert I am left with the same issue at the beginning where I cannot access the results outside of the IF statement. – Deployment Failure Sep 19 '18 at 18:48
2

This code:

SObject[] sobs = [select Id, Name from Contact limit 5];
Database.upsert(sobs, Contact.Id, true);

generates the error:

Upsert with a field specification requires a concrete SObject type

and this code does not:

SObject[] sobs = [select Id, Name from Contact limit 5];
Database.upsert((Contact[]) sobs, Contact.Id, true);

which is pretty surprising; I guess the compiler injects the static type of sobs in some way rather than the method internally looking at the type of sobs at runtime.

So if you want to use that method, you are stuck with having explicitly types.

|improve this answer|||||
  • This is only true, when using the AllOrNone command. – Deployment Failure Sep 19 '18 at 18:47
  • Those poor SOBs. – Adrian Larson Sep 19 '18 at 18:48
  • @DeploymentFailure Yeah might be limited to that method but the error message suggests that it is having the field specification that triggers the need for the type. – Keith C Sep 19 '18 at 18:49
0

OK so I was able to complete this using the following:

    global Database.UpsertResult[] results;

    // execute method.....

    Schema.SObjectField f = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap().get(extKey);
    List<sObject> toUpsert = (List<sObject>)Type.ForName('List<' + objectName  + '>').newInstance();
    for(sObject dynObject : lstObjects) {
        toUpsert.add(dynObject);
    }

    if(toUpsert instanceOf List<Account>){
        results = Database.upsert((List<Account>)toUpsert, f, false);
    } else if(toUpsert instanceOf List<Contact>){
        results = Database.upsert((List<Contact>)toUpsert, f, false);
    }

    system.debug(toUpsert);
    system.debug(results);
|improve this answer|||||
  • You don't need to do the dynamic cast at all. It adds nothing here. also your if/else chain is functionally equivalent to what Pranay and I recommended. – Adrian Larson Sep 19 '18 at 19:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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