[SalesForce] Processing callouts from Batch

I have a tough scenario where a future method should be called from a batch.I came across various articles stating that it is not possible.
My scenario is i need to send emails for contacts of account .Where the Account id is passed from the batch class and i have scheduled the batch.When i try callouts without future method i get error

You have uncommitted work pending. Please commit or rollback before calling out

Is there any way for solving this problem
Formation of my class I am not using any http callouts and webservices

global class batch implements batch,schedule,Database.callouts
{
      global void execute(schedulableContext sc)
      {
         /**scheduling batch**//
      }
      global Database.queryLocator start(Database.BatchableContext BC)
      {
           /***collecting data through query and passing list to execute ***/
      }
      global  void execute(Database.BatchableContext BC,List<Service__c> s)
      {
          /***processing the input list and obtaining  list of Ids***/
          /****batchclass.emailcallout(list<id>)   emailcallout is future method***/
      }   
       finish{}
}

public class batchclass
{
     @future(callout=true)
     public static emailcalllout(list<id> ser)
     {
         /***some processs***/
        emailsend es = new emailsend();//calling another class to invoke email  sends   to contacts of particular accounts
        es.sendmail(id,list<>);//account id and list of contacts
     }
}

Best Answer

Move your callouts before your DML statements.

In other words make sure that all update, insert and delete statements in your execute method occur after any callout statements. A quick search of "uncommitted work" will reveal that there are plenty of questions on this site that address this issue

Here's an example :

Assume you have a queue of "contacts" off accounts, each contact has a url (Url__c) and a state (State__c) . State would be set to EMAIL-PENDING just before you make your callout

This would be a problem and fire the uncommited error :

public void execute( ... )
{

    for(Sobject s : contacts)
    {
        contact.put('State__c', 'EMAIL-PENDING');
        update contact;

        // http callout (contact.Url__c)

        // send-email( contact.emailAddress )'
        contact.put('State__c', 'EMAIL-SENT');
        update contact;
    }
}

Possible solution : modify your logic so that you focus on state showing the emails actually sent ( EMAIL-SENT )

    public void execute( Database.BatchableContext BC, List<SObject> contacts )
    {

        for(Sobject s : contacts)
        {
             // http callout (contact.Url__c)

            // send-email( contact.emailAddress )'
            contact.put('State__c', 'EMAIL-SENT');
            update contact;
        }
    }

Alternative solution, move from a query locator to an iterable in your batch and use the start context to set all work as EMAIL-PENDING. This approach allows you to perform state changes / DML before you make the callout

   global Iterable<...> start(Database.BatchableContext info) {

      for(Sobject s : contacts)
        {
            contact.put('State__c', 'EMAIL-PENDING');
            update contact;
        }

        return....
    }


    public void execute( Database.BatchableContext BC, List<SObject> contacts )
    {
        for(Sobject s : contacts)
        {
            // http callout (contact.Url__c)
            // send-email( contact.emailAddress )'
        }

        for(Sobject s : contacts)
        {
            contact.put('State__c', 'EMAIL-SENT');
            update contact;
        }
    } 
Related Topic