1

I just got a strange recommendation from SalesForce support which was related to inability of APEX controller class to save value from VisualForce component to a custom setting.

The recommendation was literally "DML inside the property's getter and setter are not a good practice."

Here is what does not work:

...
    public String currentFunction {
        get {
            if (null == currentFunction) {
                return getCurrentFunction();
            }
            return currentFunction;
        }
        set {
            currentFunction = value;
            // these lines:
            TestSetting__c settings = TestSetting__c.getInstance();
            settings.Function__c = value;
            upsert settings;
        }
    }
...

The controller code above does not produce any exceptions, logs show the setting has been saved, but in reality it does not. I found nothing explaining that anywhere, including Apex Properties page on the dev guide.

I spent a month or so to solve this issue with the support, so i decided to share (partial!) solution for this case to give you a chance to not waste your time.

Update #1: as per Controller Methods dev guide,

It’s a best practice for setter methods to be idempotent, that is, to not have side effects. For example, don’t increment a variable, write a log message, or add a new record to the database. Visualforce doesn’t define the order in which setter methods are called, or how many times they might be called in the course of processing a request. Design your setter methods to produce the same outcome, whether they are called once or multiple times for a single page request.

IMO, that does not directly explain my case, but gives some useful recommendations. but my biggest concern is — why does that happen silently?

2
  • 1
    Potentially relevant: Is DML not allowed in a Visualforce getter?
    – Adrian Larson
    Commented Mar 21, 2017 at 19:17
  • @adrian-larson, there is "We recommend that your get accessor should not change the state of the object that it is defined on" statement on the Apex Properties guide. in my case i wanted to save selector's value as a preferred user setting. in setter, ofc.
    – hlopetz
    Commented Mar 21, 2017 at 19:30

1 Answer 1

0

Solution for VisualForce

The solution is to do the DML in a separate method called by action call from the VF page/component.

Here is the controller class:

public with sharing class TmpController {
    public String noFilter { get { return 'No Filter'; } }
    public String currentFunction {
        get {
            if (null == currentFunction) {
                return getCurrentFunction();
            }
            return currentFunction;
        }
        set;
    }
    public SelectOption[] getPossibleFunctions() {
        return new SelectOption[]{
            new SelectOption('One', 'One'),
            new SelectOption('Two', 'Two'),
            new SelectOption('Three', 'Three')
        };
    }
    // here we do the DML
    public PageReference updateCSInfo() {
        TestSetting__c settings = TestSetting__c.getInstance();
        settings.Function__c = currentFunction;
        upsert settings;
        return null;
    }
    private String getCurrentFunction() {
        TestSetting__c settings = TestSetting__c.getInstance();
        return (null == settings.Function__c) ? noFilter : settings.Function__c;
    }
}

... and here is the VF page:

<apex:page controller="TmpController">
<apex:form>
<apex:pageBlock title="Tmp Page">
<apex:pageBlockSection>
<apex:pageBlockSectionItem >
<apex:outputLabel for="functionSelector" value="Show Stuff Only For:" />
<apex:selectList value="{!currentFunction}" title="Select a Function" size="1" id="functionSelector">
    <apex:actionSupport event="onchange" action="{!updateCSInfo}" />
    <apex:selectOption itemValue="{!noFilter}" itemLabel="No Filter" />
    <apex:selectOptions value="{!possibleFunctions}" />
</apex:selectList>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>

hopefully, that will be helpful.

1
  • 2
    Another note: you can do DML in a setter if you move your DML into another method and call that method from within the setter. I've done this on occasion as I like using setters as essentially an onchange event with a single parameter of "value". Generally, you shouldn't try to put DML in setters though. The only example I have is our ErrorLogger that creates a case when code fails. If a setter fails for xyz reason, we create a case using our ErrorLoger.
    – gNerb
    Commented Mar 21, 2017 at 19:10

You must log in to answer this question.

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