1

I am trying to perform the following procedure:

String tipoInt = 'Account';

List<SObject> sl = new List<SObject>();

Sobject so = Schema.getGlobalDescribe().get(tipoInt).newSObject();
so.put('name', 'Endrit');
so.put('Test_External_Id__c', '1');
sl.add(so);

Sobject so2 = Schema.getGlobalDescribe().get(tipoInt).newSObject();
so2.put('name', 'Dori');
so2.put('Test_External_Id__c', '2');
sl.add(so2);

upsert sl  Account.Fields.Test_External_Id__c;

According to this reference: https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_dml_section.htm#apex_dml_upsert

This code should work. Instead it returns the following error:

Upsert with a field specification requires a concrete SObject type

I am choosing a different path to avoid this obstacle, but if I could solve it through this approach, would save me a lot of work.

2
  • 1
    Did you try without Account.Fields.Test_External_Id__c in your upsert ?
    – SF_user
    Commented May 22, 2015 at 13:24
  • It won't work, because you should specify explicitly the external Id to perform an Update or insert later. Commented May 22, 2015 at 16:29

4 Answers 4

2

The error message tells you all you need to know. To use upsert with an external ID field you need to be upsetting a list of a concrete type, e.g. Account, rather than a list of sObjects.

If you need to upsert a list of generic sObjects then you can't specify an external ID field and must instead rely on the standard Salesforce ID field.

From your example it's hard to see why you are doing things the way you are. Both of the records are Account objects and you are referencing a field on Account for the upsert.

You could change your code to the following and everything would be fine.

List<Account> sl = new List<Account>();

Account so = new Account(Name = 'Endrit', Test_External_Id__c = '1'); sl.add(so);

Account so2 = new Account(Name = 'Dori', Test_External_Id__c = '2'); sl.add(so2);

upsert sl Account.Fields.Test_External_Id__c;
2
  • Actually, I want to know if I'm able to apply that type of upsert operation. Because, I have a use case, where I want to upsert a variety type of SObjects. The reason why I used in the example 2 Accounts, is just for demonstration. I know that I could use a List<Account> instead of List<SObject>. The reason I used that list, is to show that what is described in the Language Reference, is eaither falsy, or I have some logical problem in my implementation. Commented May 22, 2015 at 14:53
  • @EndritSino The documentation is correct, if not somewhat misleading. It accepts a list of SObjects, because any object can be upserted, but it requires a concrete sobject type because upsert only works on one object at a time. For other "weird" examples, consider the Comparable interface, which requires a function Integer compareTo(Object other), even though clearly "other" will always be of the same type as the class that implements it.
    – sfdcfox
    Commented May 22, 2015 at 15:48
0

It seems you are manipulating Account object. So try this :

List<Account> sl = new List<Account>();

Account so = new Account();
so.Name = 'Endrit';
so.Test_External_Id__c = '1';
sl.add(so);

Account so2 = new Account();
so2.Name = 'Dori';
so2.Test_External_Id__c = '2';
sl.add(so2);

upsert sl  Account.Fields.Test_External_Id__c;
0

Did you tried writing only field api name afert upsert:

upsert sl Test_External_Id__c;

It worked for me, may this could help.

1
  • No, it can't work, because it accepts only Schema.SOfields type after the list of SObjects. I don't know what code you tried to run, but I copy pasted mine, and just changed the line that you indicated. It can't work that way. Commented May 22, 2015 at 15:31
0

If you want to upsert a List<SObject> you need to split the operation into an insert and an update. But first, you'll need to set the record Ids from the External Id. Since you need to do this for a variety of SObjects, you'll need a way to query the External Id. Here are two ways you could do this:

  1. "Interface" Method: For each SObject you want to do this operation on, create a formula called External_Id__c (or something relating to the operation). In this formula you return the value of the External Id field. Then you can query records from your External Id formula. I call this method the "Interface" method because, like an interface, you are requiring your SObjects to have a getter which returns the External Id.

    // Suppose you are getting your data from a Web Service.
    // In your Web Service's data classes, add an interface (maybe call it "Upsertable").
    // In your interface, have a method getExternalId():
    // You'll also need to get the Salesforce Id for WebServiceData, have another method that returns the SObjectType:
    
    public Interface Upsertable {
        String getExternalId();
        SObjectType getSObjectType();
    }
    
    public class WebServiceData implements Upsertable {
        public String Name;
        public Integer DataExternalId;
        ...
    
        public String getExternalId(){
            return String.valueOf(this.DataExternalId);
        }
    
        public SObjectType getSObjectType(){
            return Web_Service_Data__c.SObjectType;
        }
    }
    
    public static SObject getWebServiceDataSObject(WebServiceData data){
        // set all the SObject fields from data.
    }
    
    
    .... ....
    
    
    List<WebServiceData> webServiceDataList;
    Map<String, SObject> sObjectMap = new Map<String, SObject>();      
    for(WebServiceData data :webServiceDataList){
        sObjectMap.put(
            data.getExternalId(), 
            getWebServiceDataSObject(data)
        );
    }        
    
    // Get the SObjectType somehow.  
    // Use the interface method we added.
    SObjectType = webServiceDataList.isEmpty() ? null : webServiceDataList[0].getSObjectType();
    String sObjectName = sObjectType == null ? null : sObjectType.getDescribe().getName();
    
    Map<String, Id> sObjectIdMap = new Map<String, Id>();
    for(SObject record : Database.query(
        'SELECT Id, External_Id__c ' +
        'FROM ' + sObjectName + ' ' +
        'WHERE External_Id__c IN :externalIdSet'
    )
        sObjectIdMap.put((String) record.get('External_Id__c'), (Id) record.get('Id'));
    
    for(String externalId :sObjectMap.keySet(){
        SObject record = sObjectMap.get(externalId);
        record.put('Id', sObjectIdMap.get(externalId);
        sObjectMap.put(externalId, record);
    }
    
  2. Map Method: Create a Map<SObjectType, String> which provides the External Id Field for each SObjectType.

    public static final Map<SObjectType, String> SObjectTypeExternalIdMap = new Map<SObjectType, String>{
        My_SObject__c.SObjectType, 'External_Id_1__c',
        Product__c.SObjectType, 'Product_Id__c',
        ...
    };
    
    ... ....
    
    List<SObject> sObjectList;
    // Get SObjectType
    Id firstSObjectId = sObjectList.isEmpty() ? null : (Id) sObjectList[0].get('Id');
    SObjectType sObjectType = firstSObjectId == null ? null : firstSObjectId.getSObjectType();
    String externalIdField = SObjectTypeExternalIdMap.get(sObjectType);
    String sObjectName = sObjectType == null ? null : sObjectType.getDescribe().getName();
    
    Map<String, Id> externalIdMap = new Map<String, Id>():
    for(SObject record : Database.query(
        'SELECT Id, ' + externalIdField + ' ' +
        'FROM ' + sObjectName + ' ' +
        'WHERE ' + externalIdField + ' IN :externalIdSet'
    ))
        externalIdMap.put((String) record.get(externalIdField), (Id) record.get('Id'));
    

    for(SObject record :sObjectList) record.put('Id', externalIdMap.get((String) record.get(externalIdField)));

With externalIdMap, set each SObject's Id from its External Id. And when it's time to "upsert", create a list for inserting and another for updating.

List<SObject> sObjectList;
List<SObject> sObjectListToInsert = new List<SObject>();
List<SObject> sObjectListToUpdate = new List<SObject>();

for(SObject record :sObjectList){
    List<SObject> upsertList = (Id) record.get('Id') == null ? sObjectListToInsert : sObjectListToUpdate;
    upsertList.add(record);
}

insert sObjectListToInsert;
update sObjectListToUpdate;

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .