Question - How can I determine that an sObject is read-only (due to being part of Trigger.New
in an after trigger or otherwise).
Explaination - I've been making a method to try and generically get lookup records for a list of records and put them on correct sObjects in the list (as if I had done an individual SOQL query for each one), but I'm running into an issue when the method is run in an after trigger - System.FinalException: Record is read-only
. The issue is that the records in Trigger.New
are read-only in after triggers, and apparently this also applies to the putsObject
method. That's somewhat OK for me, since my method also returns a Map<ID, sObject>
with all the lookup records which can be used instead, but my issue is that FinalException
apparently is uncatchable, so I need to determine whether the sObjects in the list are read-only before attempting to use putsObject
. For reference, here is the method:
public static Map<ID, sObject> populateRelatedLookup(List<sObject> records, string field, string relatedSObject, List<String> relatedObjectFields)
{
Set<String> relatedRecords = new Set<String>();
for (sObject i : records)
{
relatedRecords.add((String)i.get(field));
}
Map<ID, sObject> relatedMap = new Map<ID, sObject>(Database.query('select ' + String.join(relatedObjectFields, ',') + ' from ' + relatedSObject + ' where ID in :relatedRecords'));
string fieldAsReference = field.replace('__c', '__r');
Map<ID, sObject> returnMap = new Map<ID, sObject>();
for(sObject i : records)
{
sObject record = relatedMap.get((String)i.get(field));
i.putSObject(fieldAsReference, record); // <------ Issue line
returnMap.put(i.id, record);
}
return returnMap;
}
For now, I've changed the code to check whether we are attempting to modify a variable in Trigger.New
in an after trigger, but this only works if the user is actually passing Trigger.New
to the method literally:
public static Map<ID, sObject> populateRelatedLookup(List<sObject> records, string field, string relatedSObject, List<String> relatedObjectFields)
{
Set<String> relatedRecords = new Set<String>();
for (sObject i : records)
{
relatedRecords.add((String)i.get(field));
}
Map<ID, sObject> relatedMap = new Map<ID, sObject>(Database.query('select ' + String.join(relatedObjectFields, ',') + ' from ' + relatedSObject + ' where ID in :relatedRecords'));
string fieldAsReference = field.replace('__c', '__r');
Map<ID, sObject> returnMap = new Map<ID, sObject>();
for(sObject i : records)
{
sObject record = relatedMap.get((String)i.get(field));
if(!Trigger.isExecuting || records != Trigger.New || Trigger.isBefore)
{
i.putSObject(fieldAsReference, record);
}
returnMap.put(i.id, record);
}
return returnMap;
}
Trigger.New
.if(records !== Trigger.new || !Trigger.isExecuting || Trigger.isBefore)
for performance reasons.