[SalesForce] Object not found issue in PDF Attachment visualforce page for an email

We have a scenario where we have to add dynamic content in attachments from apex. On click of a button, I insert a list of Contacts. And in the next line, I pass it's id as parameter for a visualforce page which is rendered as PDF and used for the attachment. So this is done for each Contact in the list. But, when the .getContent() method is called, I get a visualforce exception : Unable to retrieve object. The same code works when called from a button on the record detail page. But calling this just after performing insert gives me this error.

/* code snippet */
insert lstContacts;

for(Contact c : lstContacts)
{
     sendEmail(c.Id);
}

...
...
private void sendEmail(String recordId)
{
   Pagereference pdf = Page.AttachmentPage;
   pdf.getParameters().put('id',recordId); 
   pdf.setRedirect(true);
   // Take the PDF content
   Blob b = pdf.getContent(); // HERE I GET THE EXCEPTION
}

It is a StandardController page and works fine for records already existing. Is there a limitation for not being able to access a record's standard controller right after inserting it? Need some help here.

Best Answer

I have seen this before. I haven't seen where it is documented, though.

When you pass the ID to the PDF controller directly after the insert from the same controller method the record is not available. You can output the ID in the PDF and see that it is clearly getting passed through. You can select the record back in the method where you did the insert, but for some reason the record is not available in the PDF controller. I suspect there is something going on behind the scenes with how the transactions and/or transaction contexts are handled.

You can work around it by setting the parameter and then forwarding to a blank page with an action that fires and gets the PDF for you. So, after the insert you forward to Page.IntermeditateRedirect and then in that page have something like:

<apex:page action="{!getPdf}" controller="YourPdfController/>

Or, if you want to use the same controller then have an action but set different modes and use the action as a controlling method. A co-worker of mine came up with an approach like that, implemented roughly as follows:

<apex:page action="{!handleMode}" controller="YourController"/>

Then in the controller:

public String mode { get; set; }
public YourController() {
     mode = System.currentPageReference().getParameters().get('mode');
 }
 public PageReference handleMode() {
   if (Mode  == 'renderPdf') {
       return rednerPdf();
   } else if (mode == 'insertRecord') {
       return insertRecord();
   }
 }
 public PageReference insertRecord() {
    // do the insert of the rec...then redirect to the same page
    PageReference pageRef = new PageReference('url');
    pageRef.getParameters().put('id', rec.Id);
    pageRef.getParameters().put('mode', 'renderPdf');
    pageRef.setRedirect(true);

   // after the redirect the handleMode will get called and the rednerPdf will get executed.
 }
 public PageReference renderPdf() {
    // will be able to select a record and see it based on the id parameter
 }