Search code examples
salesforceapex-codevisualforce

Salesforce error while makin a call to a web service api


I get the following error when I call the function add() You have uncommitted work pending. Please commit or rollback before calling out

I call the getItems() to populate the drop down and then the add function to insert the selected item from the drop down

 public PageReference add() {
              insert technology;
              return null;
            }

public List<SelectOption> getItems() {
    List<SelectOption> options = new List<SelectOption>();
    List<Technology__c> AddedT=[SELECT Name FROM Technology__c];
    HttpRequest req = new HttpRequest(); 
    req.setMethod('GET');
    req.setEndpoint('http://submit.toolsberry.com/sfdc/technologies');
    Http http = new Http();
    HTTPResponse res = http.send(req);  
    String response=res.getBody();
    XmlStreamReader reader = new XmlStreamReader(response);
     List<String> AllTech = new List<String>();
     while(reader.hasNext()) {    
     if (reader.getEventType() == XmlTag.START_ELEMENT) {
        if ('string' == reader.getLocalName()) {
    while(reader.hasNext()) {
     if (reader.getEventType() == XmlTag.END_ELEMENT) {
        break;
     } else if (reader.getEventType() == XmlTag.CHARACTERS) {
        String tname = reader.getText();
        AllTech.add(tname);
    }
    reader.next();
 }
        }
     }
    reader.next();
 }
}

Solution

  • This is because you need to do all your DML AFTER you are done with any callouts, not before. So any insert/update/upsert or delete statements must follow any http.send(req); calls.

    ** Looks like your list is getting repopulated after you call the add() method, because your list resides in a getter method **

    This is thread-specific and must occur in the sequence per any given thread. So, for example, when a user clicks a button with an action method, all DML statements in that call must follow any callouts that happen in the same thread. Same for a trigger or batch Apex.

    Having a getter/setter somewhere that is updating data somehow can cause this. Eg:

    public String someProperty
    {
       get
       {
          return [SELECT Name FROM CustomObject__c WHERE Id = :this.someId];
       }
    
       set(String s)
       {
          CustomObject__c c = [SELECT Name FROM CustomObject__C WHERE Id = :this.someId]
          c.Name = s;
          update c;
       }
    }
    

    Also, never put a callout in a getter. Always put a callout in an explicit method that does it once and only once. Getters will get fired multiple times and callouts have strict limitations in Apex.