The exception is coming from the getRecordTypeInfos()
call, not the getDescribe()
call. The describe governor limit only applies to a small number of describe calls listed in the governor limit documentation. The two that people typically run into are fields
and getPicklistValues
. This one was new to me.
You appear to want to build a collection of sObject describes, but only if they have record type enabled (and are currently accessible by the running user). I suspect you are going to have to come up with a different way of determining this, or filter out more objects before you make the getRecordTypeInfos
call in the first place.
If you want to find out all sObjects that support record type in the first place, the RecordType object has an sObjectType field that is of type picklist. You could determine the sObjects that support record type at all by making a describe call for the picklist values of that field as follows:
Set<String> sObjectsThatCanHaveRecordType = new Set<String>();
Schema.DescribeFieldResult field = RecordType.sObjectType.getDescribe();
List<Schema.PicklistEntry> entries = field.getPicklistValues();
for (Schema.PicklistEntry entry : entries){
sObjectsThatCanHaveRecordType.add(entry.getValue());
}
Then, in your code above, instead of calling getRecordTypeInfos
, check that the current sObjectType is in the set:
if(sObjectsThatCanHaveRecordType.contains(ds.getLocalName()))
objectsUSE.add(ot);
Note: I'm not certain whether you would use getName or getLocalName here. I don't know if the RecordType.sObjectType field stores the qualified sObject name where namespaced sObjects are used, or the unqualified name as I currently don't have an org with a namespaced package installed.
But this is imperfect as any object that supports record type could be used. So maybe a better approach would be to use a query for the sObjectType field of the RecordType object. Create a set collection containing all active record type sObjectType field values. Then use the same logic that I used above and determine whether or not you have a record type enabled object from the set. I did it this way.
Set<String> rtObjNames = new Set<String>();
for (RecordType rt : [select sObjectType from RecordType where IsActive = true]) {
rtObjNames.add(rt.sObjectType);
}
System.debug(rtObjNames);
Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
List<SObjectType> objectsUSE=new List<SObjectType>();
for (String objName : rtObjNames)
{
Schema.SObjectType ot = gd.get(objName);
if(ot.getDescribe().isCreateable())
objectsUSE.add(ot);
}
System.debug(objectsUSE);
Of course, this has downsides as well. First of all, you burn a query. Bummer that, but not the end of the world. Second, in a very large organization that heavily leverages record types, you could eat into the 50k query rows governor limit (let's say 50 object, 10 record types, that's 500 rows = 1% of my total limit)...so I'd be sure to manage this if your code is using other queries elsewhere.
In the end, I think this last option is going to give you the best chance of success.
EDIT: I had a rethink of how I initially solved this and it occurred to me that once I had queried the record types in use from RecordType, I should just use the global describe map, so I refactored the for loop to go over set of strings instead of the entire list of sobject types in the global describe map. Duh! Much better.
You're right, it's because you have DML inside of a loop. Instead, add the new records to a list or a map and then do your insert at the end. I've provided an untested-sample:
Edit
I missed the need to store off values from the Opportunity into the other variables. I've switched to using a map, keyed on the Opp id, to quickly get these values in a secondary loop.
trigger CreateAndUpdatefEvent on Opportunity (after insert,after update) {
list<Id> OppIdList = new list<Id>();
list<fEvent__c> felist = new list<fEvent__c>();
list<fEvent__c> felist2 = new list<fEvent__c>();
// Store new records in the map
Map<Id,Event_Controller__c> newEc = new Map<Id,Event_Controller__c>();
fEvent__c tempfevent = new fEvent__c();
for(Opportunity o:trigger.new){
OppIdList.add(o.Id);
}
list<Opportunity>insertOpptyList =[Select o.StageName,o.IsClosed,(Select Id, Name, Amount__c Opportunity__c From fEvent3__r where Status__c!='Cancelled' limit 1) From Opportunity o where o.Id in:OppIdList];
for(Opportunity o:insertOpptyList){
if(o.fEvent3__r.size()==0){
// Add new records to the map instead of one-off insertion
newEc.put(o.id, new Event_Controller__c(Amount__c = o.Amount,Years__c =1,Fiscal_Year__c = string.valueOf(o.CloseDate.year())));
}
else{
if(o.fEvent3__r.size()>0){
o.fEvent3__r[0].Amount__c = o.Amount;
felist2.add(o.Funding_Event3__r[0]);
}
}
}
// Do your DML here, outside of the loop.
insert newEc.values();
for(Id index: newEc.keyset()){
tempfevent.Opportunity__c = index;
tempfevent.Event_Controller__c = newEc[index].Id;
tempfevent.Amount__c = Trigger.newMap[index].Amount;
felist.add(tempfevent);
tempfevent = new fEvent__c();
}
if(felist.size()>0)
insert felist;
if(felist2.size()>0)
update felist2;
}
Best Answer
Look in the code that you have not done any describe call inside the for loop
This is an example for field describe .Search for the describe calls in the code and make sure we have out of the for loop.
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_fields_describe.htm
Here is the document of complete list of fielddescribe calls .
There is a limit that in a single context run you can have only 100 fielddescribe calls.This is one of the governor limits of platforms
Here is the class thats causing issue .I see getdescribe inside the for but that may not be issue but please dont call this inside the for loop .Try to bulkify
As shown above by Andrew Fawcett you may get in map and this should resolve the issue