Last week, there was a discussion on Yammer on how to get values from the “Sent Email” record when all fields are marked as Internal. I was surprised that many people do not know what can/can’t access modifiers (such as local, protected, or internal) be used for. I thought that this was widely known information, but based on the number of questions I got on Yammer and later through LinkedIn, I found the opposite.
Let’s start with a short summary. What are the access modifiers?
Access modifiers are used to set accessibility of tables, table fields, codeunits, and queries, which controls whether the object can be used from other code in your module or other modules. Access modifiers in AL are designed to create solid APIs, by limiting the symbols that dependant modules can take a reference on. Limiting the API surface can hide implementation details and allow for later refactoring of code without breaking external code.Using Access Modifiers in AL – Business Central | Microsoft Learn
In BC, we have 4 modifiers (not all of them can be used everywhere).
|The object or field can be accessed by any other code in the same module and in other modules that reference it and that are allowed in the app.json (internalsVisibleTo).|
|The field can be accessed only by code in the same table or table extension where the field is defined.|
|The field can be accessed only by code in the same table or table extensions of that table.|
|The object or field can be accessed by any other code in the same module and in other modules that references it.|
Now you can think: OK, this is a really good way to protect my data. If I have a field that should not be modified by an external app, I will set it as local or internal, and no one can access or even change the field. NO! This is not how the modifiers work in the AL Language. If you open the link above to the Learn portal, you will find purple and blue notes:
What does it mean? You should NEVER use access modifiers to protect your data or rely on access modifiers when hiding values such as keys or login details.
How to access internal, protected or local fields?
It’s simple. The only things you need are one RecordRef and one FieldRef variable, and know the IDs of the field you want to access. See example below – Sent Emails has all fields set as Internal.
[EventSubscriber(ObjectType::Codeunit, Codeunit::Email, 'OnAfterEmailSent', '', false, false)] local procedure OnAfterEmailSentEmail(SentEmail: Record "Sent Email") var RecordRef: RecordRef; FieldRef: FieldRef; begin RecordRef.GetTable(SentEmail); FieldRef := RecordRef.Field(6); // Description Message('%1', FieldRef.Value()); FieldRef := RecordRef.Field(7); // Date Time Sent Message('%1', FieldRef.Value()); FieldRef := RecordRef.Field(13); // Sent From Message('%1', FieldRef.Value()); end;
The same approach can be used to create indirect dependencies (use fields from other extensions without creating dependency in the app.json file). Why would you do that? Well, it really depends on your requirements, but for example, when you have an ISV solution where you need information from localization extension (such as Registration No. of customer /Czechia/ or ABN /Australia/etc.). You can create another extension that will extend your solution, or you can test whether the field exists (recordref + fieldref) and, if the field exists, get the value.