8

Once I had the illusion that accessing custom setting is always free and does not consume any governor limits. Until today: I've learned the hard way that this is only half-true.

It is true, that this comes without the cost of a query:

MyCustomSetting__c.getall().values();

But this DOES cost you a query

database.query('select Id from MyCustomSetting__c ');

Now how can I get the setting dynamically in a way that my code runs on every org regardless of the existence of MyCustomSetting__c? I need to hide the setting from Salesforce so that I can use something inside of a try/catch. If the setting does exist I need to get some values. If it does not exist I fall back on default values.

Additionally I need a way to update records of the settings synchronously in trigger contexts.

Questions

  • Can this be done free of charge of any query limit?
  • Where is the documentation that using database.query() for CustomSetting cost limits? I can not find it. If there is no documentation, I would assume that either the documentation is wrong or incomplete OR the limit consumption is a BUG.

Update 2021-11-24

I've tried to use CustomMetadataType instead. It failed.

CustomMetadataType (CMT) is similar to CustomSetting (CS) and CMT looked very promising at the first glance. I watched this video from 2019 where CMT is advertised as more powerful successor of CS. Especially this slide convinced me to give them a shot:

enter image description here

BUT in fact CMT is unusable for me, because it does not support DML to update records. Records can be updated asynchronously with APEX. Not sure if this works in trigger context, but the possible timing issues are not acceptable for my requirements. I need updates of records in sync.

So I would not agree that CMT is on par with CS in all aspects. DML and in sync updates are crucial for some scenarios.

2 Answers 2

6

There's no way to do this directly; there's no unique common parent type or interface, we can't access namespace properties dynamically, we don't have proper language reflection, etc. Basically, the type system in Apex isn't robust enough to allow us to do this.

The closest you can get to would be to introduce an interface and wrapper classes.

interface iCustomSetting {
  public Map<Id, sObject> getAll();
}
public class MyCustomSetting implements iCustomSetting {
  public Map<Id, sObject> getAll() {
    return MyCustomSetting__c.getAll();
  }
}

Which you then use dynamically:

public static Map<Id, sObject> getAllForCustomSetting(String settingName) {
  iCustomSetting setting = (iCustomSetting)(Type.forName(settingName).newInstance());
  return setting.getAll();
}

You still have to write a new class for each custom setting, but now you can dynamically select from any of those custom settings by name.

It would be nice if we had some way to do this directly, but that just doesn't exist in Apex. If you really want to save those SOQL limits, and want dynamic access, you have to build it yourself.

As a side effect, you still have a hard-coded reference to the setting, so it must exist in the org. You could, I suppose, package additional extension packages so that the setting and the interface implementation are in can be installed separately, with the interface in the core package.

8
  • as always a brilliant answer. Even if it is a fatal answer for my requirements. Could Custom Metadata give me a substitute for what I need: dynamic access plus zero limit consumption in terms of SOQL? Or would an investigation be a waste of time to solve this specific aspect?
    – Uwe Heim
    Commented Nov 16, 2021 at 13:27
  • 2
    @UweHeim CMDT doesn't count against governor limits, assuming certain requirements are met. They may serve your purpose better than custom settings.
    – sfdcfox
    Commented Nov 16, 2021 at 13:31
  • Just note that CMT queries are actually quite limited - there is not support for OR, just AND, in the WHERE clause. OR can seem to work sometimes, but not always. Of course, there is now a "static" way to load them as well.
    – Phil W
    Commented Nov 16, 2021 at 13:49
  • @PhilW Yeah, but as a general "get all for free" type mechanism, it works just fine.
    – sfdcfox
    Commented Nov 16, 2021 at 14:14
  • @sfdcfox now I've tried CMT. It would be perfectly fine in terms of READ. Using the same database.query() I've used for CustomSettings. I can confirm that with CS it cost limits and with CMT its "free". BUT what shocked me is the fact that DML is not possible for records. I need to update CMT-records in trigger context and it should happen synchronously and NOT async. Salesforce pain: here you get this but not that, and there you get that but not this. For me neither CMT nor CS are usable. IMHO all CS queries should be free, like CMT. Is there a doc that CS cost limits if queried? Maybe a bug?
    – Uwe Heim
    Commented Nov 24, 2021 at 9:40
2
+50

Custom Settings Methods mentions this important difference between querying the Custom Setting directly versus using its provided methods.

All custom settings data is exposed in the application cache, which enables efficient access without the cost of repeated queries to the database. However, querying custom settings data using Standard Object Query Language (SOQL) doesn't use the application cache and is similar to querying a custom object. To benefit from caching, use other methods for accessing custom settings data such as the Apex Custom Settings methods.

As such, it's expected that using SOQL to query Custom Settings would count against your query limits. Your best bet would be to leverage the other answer or optimize, as you've probably done, to make sure you're limiting the amount of queries you need to make.


The decision between using a Custom Setting vs. Custom Metadata Type is certainly one that requires looking through the limitations of each. It's also important to distinguish that Custom Settings' rows are data while Custom Metadata Types' rows are metadata which help explain some of the current differences. This older answer from the lead developer of Custom Metadata Types also contains some background info

custom metadata types are primarily intended to let you develop peers of Salesforce standard metadata types, such as custom fields, tabs, or workflow rules (obviously in this first release we don’t have the power required to duplicate all the functionality in these types).

Just as you can’t perform CUD on such types in Apex today (you can’t create a new tab, modify a custom field, or delete a workflow rule in Apex), you can’t yet perform CUD on custom metadata types in Apex, short of a metadata API callout

Below are some ideas worth upvoting around these limitations:

Custom Metadata Types

Custom Settings

3
  • Thanks for that link and pointing out the exakt statement in the docs. Very good! Strangely with CustomMetadataType (CMT) using SOQL in the very same way as for CustomSetting (CS), the query is "free". IMHO this is very inconsistent and should work in the same way for CS and CMT. Probably Salesforce considers CS as legacy and CMT as the way to go for the future and is not keen on investing in CS anymore. Here I disagree fundamentally as long as CMT stays limited not to support DML. Also CS is simpler and easier.
    – Uwe Heim
    Commented Nov 25, 2021 at 13:52
  • CS should either support free SOQL OR CMT should support DML OR CS should support dynamic access to records with the functions
    – Uwe Heim
    Commented Nov 25, 2021 at 13:55
  • agree that it certainly is a tricky decision to navigate. I updated the answer to include some ideas to upvote. I think part of the issue is that they are different - I don't necessarily think of Custom Metadata type as a full replacement of Custom Settings. They serve a different need (giving users ability to create application metadata) and, as such, limitations like no-DML make a little more sense in that regard. Hopefully, they deliver that. Commented Nov 25, 2021 at 15:46

You must log in to answer this question.

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