3

I create a map of Custom Settings and then try to add a new one to it. But it throws the error

AssignByLeadSource: execution of BeforeInsert caused by: System.FinalException: Collection is read-only: External entry point

Here is the code. I removed the offending line counters.put(record.LeadSource, happydog); and tested everything else and found no errors, works great.

trigger AssignByLeadSource on Lead (before insert) {
    // Use row locks to prevent race conditions
    {
        LeadSourceCounters__c[] temp = [SELECT Id FROM LeadSourceCounters__c FOR UPDATE];
        //LeadSourceCounters is a custom setting
    }

    Map<String, LeadSourceCounters__c> counters = LeadSourceCounters__c.getAll();
    //counters is a new map that pulls in all the counts from the custom setting.

    System.debug('LeadSourceCounters__c.getAll() = ' + LeadSourceCounters__c.getAll());
    System.debug(counters);

    for(Lead record: Trigger.new) {
        if(record.LeadSource==null) {
            //if LeadSource is null, we can't round robin it specially, so continue ends for loop.
            continue;
        }

        if(!counters.containsKey(record.LeadSource)) {
        //if LeadSource doesn't exist, adds it and sets value to zero.    

            LeadSourceCounters__c happydog = new LeadSourceCounters__c(
                Name=record.LeadSource, 
                Value__c=0
            );

            counters.put(record.LeadSource, happydog);
            //put(name in map, map data is leadsourcecounter data with Name and Value)
        }

        counters.get(record.LeadSource).Value__c+=1;
        //add 1 to Value__c counter for that lead source
        record.AssignmentId__c = counters.get(record.LeadSource).Value__c;
    }
    upsert counters.values();
    //saves the updated counter for that lead source
}

I was under the impression that maps were mutable, and I could add items to them? Are there exceptions to this? How can I add a new Custom Setting to the map?

3

I suggest you first try using this map constructor to make a mutable copy of the map:

Map<String, LeadSourceCounters__c> counters
        = new Map<String, LeadSourceCounters__c>(LeadSourceCounters__c.getAll());

and leave the rest of your code as is.

  • 2
    You can also just call .clone(), which is a bit less verbose. – sfdcfox Nov 17 '15 at 23:52
  • @sfdcfox Never remember that. Though perhaps a clone of an immutable map should also be immutable... – Keith C Nov 18 '15 at 0:12
  • 1
    Thankfully, that's not the case. I actually tested it myself because of the error when I wrote up a different answer. – sfdcfox Nov 18 '15 at 1:25
  • @sfdcfox Good to know. Using an explicit mutable map constructor may be verbose but removes that uncertainty. – Keith C Nov 18 '15 at 8:59
2

I would try to do this for the map (I haven't tested but it should work):

List<LeadSourceCounters__c> listCounters = [select Name, Value__c from LeadSourceCounters__c];

Map<String, LeadSourceCounters__c> counters = new Map<String, LeadSourceCounters__c>();

for (LeadSourceCounters__c cnt : listCounters) {
  counters.put(cnt.Name, cnt);
}

I know it's more work, but that list won't be read only.

  • Thanks, I will try that. Do you know why the list is read only? – Joshua Dance Nov 17 '15 at 23:44
  • 1
    Not really, but I also didn't do a thorough research. I suspect that Maps retrieved via "getAll" are immutable, but I can't prove it. – Sebastian Kessel Nov 18 '15 at 18:28
1

Apparently a map that results from a query is immutable. So by calling clone you get a mutable map.

Map<String, LeadSourceCounters__c> counters = LeadSourceCounters__c.getAll().clone();

This works great, leaving all the rest of the code the same.

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.