5

I have a trigger (simplified sample code below) that is using field describes to determine what fields were changed to conditionally update some other field on the same record. The problem is that you are limited to 100 field describes so any mass update will break this trigger.

How can I get around this? Future methods won't work since they can only take primitive parameters and I need to know the values of any potentially changed fields. Enumerating every possible changed field is not feasible.

trigger ObjectBefore on Object__c (before update) {
    Set<String> fieldsToIgnore = new Set<String> {'custom_field1__c', 
                                                  'custom_field2__c', 
                                                  'custom_field3__c'};

    for (Object__c obj : Trigger.new) {
        if (Trigger.isUpdate) {
            Object__c old = Trigger.oldMap.get(obj.Id);

            Map<String, Schema.SObjectField> fieldMap = Schema.SObjectType.Object__c.fields.getMap();
            for (String fieldName : fieldMap.keyset()) {
                if(!fieldsToIgnore.contains(fieldName.toLowerCase()) && old.get(fieldName) != obj.get(fieldName)) {
                    obj.Custom_Field4__c = 'some value';
                    System.Debug('**** Field Changed: ' + fieldName);
                }
            }
        }
    }
}
11

Doh. I figured it out. I was being silly and including the describe operation inside my iteration loop. I only need to do that once since the object schema isn't going to change between objects. Fixed code below:

trigger ObjectBefore on Object__c (before update) {
    Set<String> fieldsToIgnore = new Set<String> {'custom_field1__c', 
                                                  'custom_field2__c', 
                                                  'custom_field3__c'};
    Map<String, Schema.SObjectField> fieldMap = Schema.SObjectType.Object__c.fields.getMap();

    for (Object__c obj : Trigger.new) {
        if (Trigger.isUpdate) {
            Object__c old = Trigger.oldMap.get(obj.Id);

            for (String fieldName : fieldMap.keyset()) {
                if(!fieldsToIgnore.contains(fieldName.toLowerCase()) && old.get(fieldName) != obj.get(fieldName)) {
                    obj.Custom_Field4__c = 'some value';
                    System.Debug('**** Field Changed: ' + fieldName);
                }
            }
        }
    }
}
  • Thanks for being man enough to post the answer rather than just quietly deleting the question! And shame on me for staring at your code and not noticing! – metadaddy Oct 2 '12 at 23:26
  • If I can save someone else from having to hang their head in shame by repeating the same problem, I'll do it gladly. – DerekLansing Oct 2 '12 at 23:38
  • 2
    Even with this, I still advise using a centralised utility that caches describes in a static map (static is transaction-scoped in Apex); once you do that it's nearly impossible to ever hit the describe governor limit. – jkraybill Oct 3 '12 at 4:35
  • 1
    @jkraybill the point you mentioned is exactly same which I highlighted in my Answer below. its always good to have schema in static member in Util class – Prafulla Patil Oct 3 '12 at 5:32
  • Totally, I had already upvoted your answer! Just wanted to make sure Derek understood the difference in approach. – jkraybill Oct 3 '12 at 7:19
9

You need to maintain schema in class as static collection(Map) and then refer that class in trigger.

Refer following code -

    // Key : SobjectAPIName  For ex. Account 
// Value : Map<String, Schema.SObjectField>, field map (k:fieldname, v:Schema.Sobjectfield)
private static final Map<String, Map<String, Schema.SObjectField>> FieldMapCache = new Map<String, Map<String, Schema.SObjectField>>();

private static final Map<String, Schema.SObjectType> GlobalDescribe = Schema.getGlobalDescribe();

// Key: sobject.field
// Value: first picklist value
private static final Map<String, String> DefaultPicklistValue = new Map<String, String>();

// can't map by Schema.sObjectType, use object name String instead
public static map<String, set<String>> ExcludedFields = new map<String, set<String>>{
    'All' => new set<String>{'OwnerId', 'LastModifiedById', 'CreatedById', 'LastModifiedDate', 'CreatedDate'},
    'Account' => new set<String>{'FirstName', 'LastName'}
};

Complete class with code you can reuse - https://raw.github.com/mbotos/SmartFactory-for-Force.com/master/src/classes/SmartFactory.cls

Thanks to awesome work done by SmartFactory for Force.com project Team

http://developer.force.com/projectpage?id=a063000000Db0CSAAZ

1

Had a chance to look at that.I feel best approach would be using custom settings and the Dynamic DML instead of schema describe calls.In custom settings we can store Tokens of the fields .Schema Describe are heavy calls .

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.