Recently, I had to create a generic framework that used Salesforce’s Database.upsert method with a generic list<SObject> since the records could be of any type. While implementing the desired functionality, various unexpected errors occurred. In this post, we’ll take a look at these various idiosyncrasies.
External Id With No “AllOrNothing” Error
When one tries to call Database.upsert using a generic list<SObject> with an external id field, the following runtime exception occurs: “System.TypeException: DML on generic List<SObject> only allowed for insert, update or delete”
The gist below demonstrates this.
External Id With No “AllOrNothing” Fix
The fix is to dynamically instantiate the List<SObject> variable to a list of the concrete SObject Type.
External Id With “AllOrNothing” Error
Database.upsert has an optional “AllOrNothing” boolean parameter that when set to false allows records that succeed to be persisted while those that error to not be persisted. When that parameter is specified with a List<SObject> and an external id, one receives the following compile time error: “Compile Error: Upsert requires a concrete SObject type at line ## column ##”
External Id With “AllOrNothing” Fix
There are at least two “workarounds”.
One is to manually do the upsert in the code. I.E. you’re writing the upsert logic in Apex instead of letting Salesforce handle it.
The other workaround is to dynamically generate a class at runtime that has an upsert method that dynamically casts a List<SObject> to a list of a concrete type and invoke the appropriate upsert call. Here’s a gist of what that looks like:
Neither workaround is particularly elegant but manually doing the upsert may be easier to implement and maintain.
What other challenges have you faced with Database.upsert and a List<SObject>? Do you have any other solutions?