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 Answers 3

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.

4
  • 2
    You can also just call .clone(), which is a bit less verbose.
    – sfdcfox
    Nov 17, 2015 at 23:52
  • @sfdcfox Never remember that. Though perhaps a clone of an immutable map should also be immutable...
    – Keith C
    Nov 18, 2015 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, 2015 at 1:25
  • @sfdcfox Good to know. Using an explicit mutable map constructor may be verbose but removes that uncertainty.
    – Keith C
    Nov 18, 2015 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.

2
  • Thanks, I will try that. Do you know why the list is read only? Nov 17, 2015 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. Nov 18, 2015 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.

You must log in to answer this question.

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