1

Trigger

Trigger ContactCountTrigger on Contact(After Insert, After Delete
, After Undelete, after Update)
{
   ContactTriggerHandler cth = new ContactTriggerHandler();
   if(trigger.isAfter)
   {
      if(Trigger.isInsert || Trigger.isUndelete)
      {
         cth.updateChlidCount(trigger.new);
      }
      else if(Trigger.isDelete)
      {
         cth.updateChlidCount(trigger.old);
      }
      else if(trigger.isUpdate)
      {
         cth.updateChlidCount(trigger.new,trigger.oldMap);
      }
   }
}

Helper Class

public class ContactTriggerHandler 
{
   Set<Id> AccounIds = new Set<Id>();
   Public Void updateChlidCount(List<Contact> Lst)
   {
      for(Contact C : Lst)
      {
        if(C.AccountId != Null)
        {
            AccounIds.add(C.AccountId);
        }
      }
      if(AccounIds.size() > 0)
      {
        this.updateParentCount(AccounIds);
      }
  }

  Public Void updateChlidCount(List<Contact> Lst,Map<Id,Contact> oldLst)
  {
      for(Contact C : Lst)
      {
          if(C.AccountId != Null)
          {
              AccounIds.add(C.AccountId);
          }

          if(oldLst.get(C.id).AccountId != Null)
          {
              AccounIds.add(oldLst.get(C.id).AccountId);
          }
      }

      if(AccounIds.size() > 0)
      {
        this.updateParentCount(AccounIds);
      }
  }

  private void updateParentCount(Set<id> Acc)
  {
     List<Account> AccLst = [SELECT id,name,number_of_contacts__c 
     ,(Select  id from contacts) FROM Account WHERE Id in : Acc];
     for(Account A: AccLst)
     {
       A.number_of_contacts__c = A.contacts.Size();
     }
     update AccLst;
  }
}
  • Suppose account1 has 2 contacts and account 2 has 3 contacts.If you use dataloader to lad data .what will happen?!! seems like account2 will update and account 1 will not – SFDC FAN Nov 12 '18 at 11:01
  • I idin't try with dataloader,Can I know which peace of code create problem and what should I update in code to work properly. – Ranjeet Yadav Nov 12 '18 at 11:11
  • Hi Annappa, I have uploaded one csv file of contact's with account id from dataloader and it will update properly count of contact records in Account object. – Ranjeet Yadav Nov 12 '18 at 11:41
  • The best way to find out if your code works in a thorough and consistent way is to write a set of quality unit tests that make assertions about your code's behavior. It looks fine to me, but human inspection is not a great guarantor of code behavior. – David Reed Nov 12 '18 at 12:19
0

The first potential problem with this code is what happens if one account in your list has more than about 2,000 contacts, at which point your trigger would fail.

A much more memory-efficient way to handle this would be an AggregateResult query:

  private void updateParentCount(Set<id> Acc)
  {
     Account[] accList = new Account[0];
     Map<Id, AggregateResult> results = new Map<Id, AggregateResult>([
       SELECT AccountId Id, count(Id) total FROM Contact
       WHERE AccountId = :Acc GROUP BY AccountId]);
     for(Id accountId: Acc)
     {
       AggregateResult ar = results.get(accountId);
       Decimal total = ar == null? 0.0: (Decimal)ar.get('total');
       accList.add(new Account(Id = accountId, number_of_contacts__c = total));
     }
     update AccList;
  }

This method will save a heap of memory, and shouldn't run into errors if there are too many contacts under a single account (within reason). There are some other optimizations you could worry about, but are largely unnecessary.

Also, be aware that without error handling, if something does go wrong (e.g. someone adds a new validation rule), it will crash, so you're leaving future problems for other people to clean up later.

A more robust solution would build a list of errors and report them back to the calling method:

  private Map<Id, String> updateParentCount(Set<id> Acc)
  {
     Map<Id, String> errors = new Map<Id, String>();
     Account[] accList = new Account[0];
     Map<Id, AggregateResult> results = new Map<Id, AggregateResult>([
       SELECT AccountId Id, count(Id) total FROM Contact
       WHERE AccountId = :Acc GROUP BY AccountId]);
     for(Id accountId: Acc)
     {
       AggregateResult ar = results.get(accountId);
       Decimal total = ar == null? 0.0: (Decimal)ar.get('total');
       accList.add(new Account(Id = accountId, number_of_contacts__c = total));
     }
     Database.SaveResult[] dmlOps = Database.update(AccList, false);
     for(Integer i = 0, s = AccList.size(); i < s; i++) 
     {
       if(!dmlOps[i].isSuccess()) 
       {
         errors.put(AccList[i], dmlOps[i].getErrors()[0].getMessage());
       }
     }
     return errors;
  }

Which you'd then check in your parent method (only one shown for brevity):

   Public Void updateChlidCount(List<Contact> Lst)
   {
      for(Contact C : Lst)
      {
        if(C.AccountId != Null)
        {
            AccounIds.add(C.AccountId);
        }
      }
      if(AccounIds.size() > 0)
      {
        Map<Id, String> errors = this.updateParentCount(AccounIds);
        for(Contact record: Lst)
        {
          String error = errors.get(record.AccountId);
          if(error != null) 
          {
            record.AccountId.addError('Failed to update parent record. The first error was: '+error);
          }
      }
  }

This will result in partial saves, works in the face of future validation rules or other events that might stop your trigger running, and uses the minimum amount of memory and CPU time while still properly handling errors.

Finally, someone's already built a solution that does all this work for you: Andrew Fawcett's Declarative Rollup Summaries for Lookups. If your goal is a fairly robust system that already has all the code written for you and is configurable via a simple user interface, you should just use what's already written instead of trying to craft your own solution.

  • Thanks for your responce,I'm new to salesforce development and working on my coding standards.This will help me a lot for my future development. – Ranjeet Yadav Nov 13 '18 at 7:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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