[SalesForce] Is DML not allowed in a Visualforce getter

Summary

I am trying to simplify some code for a page that makes a callout, then logs the request and response. I stumbled across a scenario in which the log record appears to create successfully, but then no longer exists outside the transaction boundary.

Why does the insert succeed but the record ceases to exist immediately outside the transaction?


MVR

Page

<apex:page controller="Demo">
    <apex:pageMessages rendered="{!someProperty}" />
</apex:page>

Controller

public class Demo
{
    public Boolean someProperty
    {
        get
        {
            HttpRequest request = new HttpRequest();
            request.setEndpoint('https://someUri');
            request.setMethod('GET');
            new Http().send(request);

            Integration_Log__c log = new Integration_Log__c();
            insert log;

            system.debug(log.Id);
            return false;
        }
    }
}

Error

If I take the above log.Id and navigate to the record, I get:

Data Not Available
The data you were trying to access could not be found. It may be due to another user deleting the data or a system error. If you know the data is not deleted but cannot access it, please look at our support page.

I can't see the record via query either.


Further Investigation

I changed the getter pattern to use an explicitly defined method and got a different error.

Controller

public Boolean getSomeProperty()
{
    insert new Integration_Log__c();
    return false;
}

Error

DML currently not allowed
An unexpected error has occurred. Your development organization has been notified.

And the debug log shows:

System.LimitException: DML currently not allowed

Additional Questions

Is DML not allowed in a Visualforce getter? I can't find any official reference that is the case. In fact, the only reference I can find is an eight year old blog post by Wes Nolte. It's certainly allowed in getters in general. For example placing the above getter logic into Execute Anonymous will cause the record to insert successfully.

Best Answer

Here ya go. According to Considerations for Creating Custom Controllers and Controller Extensions

  • You can't use data manipulation language (DML) operations in a “getxxx” method in a controller. For example, if your controller had a getName method, you could not use insert or update in the method to create an object

FYI: @future methods are never executed in this scenario either.


Not sure if it meets your requirements or not but you might consider using the page action as a potential workaround.

Page

<apex:page controller="Demo" action="{!doCallout}">
    <apex:pageMessages rendered="{!someProperty}" />
</apex:page>

Controller

public class Demo
{
    public Boolean someProperty
    {
        get
        {
            return false;
        }
    }

     public void doCallout(){

            HttpRequest request = new HttpRequest();
            request.setEndpoint('https://someUri');
            request.setMethod('GET');
            new Http().send(request);

            Integration_Log__c log = new Integration_Log__c();
            insert log;

            system.debug(log.Id);


     }

}
Related Topic