Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Consider the following block of code that is called on save of a basic data entry page:

public PageReference save()
{
    // accounts is binded to VF page
    for (Account a : accounts)
    {
        // assume Amount__c is never null
        a.Amount__c += 10;
    }

    try { update accounts; }
    catch (Exception e)
    {
        ApexPages.addMessages(e);
        return null;
    }

    return getPageReference();
}

Assume user makes a mistake and clicks save. The exception is thrown and caught, but the value of the Amount__c field has now gone up. If the user corrects their mistake and clicks save the Amount__c will increment by another 10.

What is the correct way of "remembering" the original field values? I am hoping caching the initial state of records is not an answer!

share|improve this question

The other two answers basically say the same thing, and they're on the right track, but you really have three choices here: (a) "cache" the old values, (b) perform your operations in a separate function, (c) reload the values back from the database, or otherwise revert them.

While the others recommend essentially cloning, this can cause a loss of error messages. Instead, backup your values, try the DML, then copy values back. That might look like this:

Account[] backup = accounts.deepClone(true, true, true);
for(Account a: accounts) {
    a.Amount__c += 10;
}
try {
    update accounts;
} catch(Exeption e) {
    Database.rollback(sp);
    for(Integer index = 0, size = backup.size(); index < size; index++) {
        accounts[index].Amount__c = backup[index].Amount__c;
    }
}

Alternatively, you can perform your updates elsewhere. That might look like this:

Boolean firstAttempt = true;
public PageReference doAction() {
    if(firstAttempt) {
        for(Account a: accounts) {
            a.Amount__c += 10;
        }
    }
    firstAttempt = false;
    // Now, do your DML, as above
}

Finally, if the records were already saved, the third option would simply be to read the values back:

public PageReference doAction() {
try {
    for(Account a: accounts) {
        a.Amount__c += 10;
    }
    update accounts;
} catch(Exception e) {
    Map<Id, Account> orig = new Map<Id, Account>([SELECT Amount__c FROM Account WHERE Id IN :accounts]);
    for(Account a: accounts) {
        a.Amount__c = orig.get(a.Id).Amount__c;
    }
}

Any of these three methods would avoid losing the original error messages, allowing the users to correct mistakes without double-work.

Note that you could also choose to simply remember how much each value was incremented by, and decrement by the same amount if an exception is caught.

Personally, I'd avoid all of these methods, and instead have the trigger perform the changes to the record. If you do this, the original values will remain untouched in the page, and will only persist if the trigger runs without errors/exceptions. The only downside to this approach would be that you'd have to reload the values from the database on a successful save if you need to show the new values later.

share|improve this answer

With your specific example you could use this technique of using temporary objects in the save method:

Account[] updates = new Account[] {};
for (Account a : accounts)
{
    updates.add(new Account(Id = a.Id, Amount__c = a.Amount__c + 10));
}
try { update updates; }

In general you can clone the objects in the save and update the clones. But yes this all amounts to "remembering" the original field values.

share|improve this answer
    
The downside of this method is that you "lose" your error messages (e.g. the clones can't tell the originals field level errors, thus can't be rendered correctly by Visualforce). – sfdcfox Jan 26 at 5:23
    
Definitely a good alternative but sfdcbox's point is valid and well taken. – Mossi Jan 26 at 9:40

you can do it with few different ways..

one option i can think of is to create a new collection and change the values like below

public PageReference save()
{
    List<Account> accsToUpdate = new List<Account>()
    // accounts is binded to VF page
    for (Account a : accounts)
    {
        Account acc = new Account(Id = a.Id, Amount__c = a.Amount__c + 10);
        accsToUpdate.add(acc);
    }

    try { update accsToUpdate; }
    catch (Exception e)
    {
        ApexPages.addMessages(e);
        return null;
    }

    return getPageReference();
}

the other option might be to have a method to revert back the values from the original collection and call that method from the catch block ..

and definitely there will be few other options

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.