Наша организация использует сочетание различных пакетов с открытым исходным кодом, включая bluewolf-beyond/selector.

Это отличный пакет, который позволяет создавать фильтры списка объектов с использованием шаблона построителя, который более удобен для чтения и повторного использования, чем классический Apex.

Например (здесь, в методе домена fflib для Assets.cls):

private List<SObject> getCancelledAssets(Map<Id,Sobject> existingRecords) {

   Select.Filter hasCancelledStatusFilter =
    Select.Field.isEqual(Asset.Status, 'Cancelled');

   if (existingRecords != null) {
    return Select.Field.hasChanged(Asset.Status)
       .andX(hasCancelledStatusFilter)
       .filter(Records,existingRecords);
   }
   else {
     return hasCancelledStatusFilter.filter(Records);
   }
}

При запуске разработчик получил следующую трассировку стека:

System.NullPointerException
LineNumber:39
Message:Attempt to de-reference a null object
Stacktrace:Class.Select.FieldReference.SchemaFieldReference.getFrom: line 39, column 1
Class.Select.FieldChangedPredicate.evaluate: line 22, column 1
Class.Select.AndPredicate.evaluate: line 51, column 1
...

Что может быть причиной?

0
cropredy 20 Дек 2018 в 20:44

1 ответ

Лучший ответ

Оглядываясь назад, ответ довольно очевиден, но поскольку пакет bluewolf-beyond/selector был довольно новый для рассматриваемого разработчика, разработчик расстроился и захотел вернуться к классическому Apex...

По сути, подсказка в трассировке стека была:

Class.Select.FieldChangedPredicate.evaluate: line 22, column 1

Это означало, что оценивался фильтр Select.Field.hasChanged(Asset.Status). А это означало, что existingRecords не равно null. Это, в свою очередь, подразумевало, что мы были в триггере обновления до/после.

Но тестовый пример был после вставки, поэтому existingRecords должен был быть нулевым.

Следуя выше в коде, доменный метод вызывал

getCancelledAssets(Map<Id,Sobject> existingRecords)

С пустой картой в обработчике onAfterInsert, поэтому if (existingRecords != null) было TRUE в случае использования после вставки!

Исправление (поскольку мы предпочитаем пустые коллекции нулям) было просто:

private List<SObject> getCancelledAssets(Map<Id,Sobject> existingRecords) {

   Select.Filter hasCancelledStatusFilter =
    Select.Field.isEqual(Asset.Status, 'Cancelled');

   if (!existingRecords.isEmpty()) {
     return Select.Field.hasChanged(Asset.Status)
        .andX(hasCancelledStatusFilter)
        .filter(Records,existingRecords);
   }
   else {
     return hasCancelledStatusFilter.filter(Records);
   }
}   

Итог — не бойтесь заглядывать в открытый исходный код, чтобы понять, что он делает, чтобы понять, что вы делаете неправильно.

1
cropredy 20 Дек 2018 в 20:44