There are two notable limitations when an enum is created as extensible.
One is that we can't use it in conjunction with the mandatory property on fields in order to force a user to select a value. The first element is probably zero (which is not valid for mandatory fields), and will usually work—we can't guarantee this behavior and must not use this technique.
The other limitation when using comparisons on enums is using relative comparisons, such as greater than. For example, the options for the SalesStatus base enum are as follows:
- None
- Backorder
- Delivered
- Invoiced
- Canceled
This enum was changed from a standard to an extensible enum in an early application update. Prior to the update, the following code would normally return a list of the SalesTable records that are not yet invoiced with SalesId and SalesName populated:
select SalesId, SalesName from salesTable where salesTable.SalesStatus < SalesStatus::Invoiced;
This will now fail with a compilation error. Extensible enums are designed to be extended by third parties, who may add their own options to the type. This means that there is no ranking in extensible enums, which means that we can't use greater than or less than expressions. We must, therefore, write code such as the following:
select SalesId, SalesName
from salesTable
where salesTable.SalesStatus == SalesStatus::Backorder
|| salesTable.SalesStatus == SalesStatus::Delivered;
The code upgrade tool in the Life Cycle Service (LCS) would look for this and assist the developer in correcting the code so that it is built correctly.
The actual values of the SalesStatus enum are stored in tables in SQL, where the values are set when the database is synchronized. The values will not change after this, but new elements that are subsequently added will be appended to this table. Since the development, OneBox VMs come with SQL Server Management Studio; you can open this, and use the following Transact-SQL against the database AxDB in order to see the way in which this data is stored:
SELECT E.[NAME], V.NAME AS SYMBOL, V.ENUMVALUE AS VALUE
FROM ENUMIDTABLE E
JOIN ENUMVALUETABLE V ON V.ENUMID = E.ID
WHERE E.[NAME] = 'SalesStatus'
This will show the following result if this enum has not been extended:
NAME |
SYMBOL |
VALUE |
SalesStatus |
None |
0 |
SalesStatus |
Backorder |
1 |
SalesStatus |
Delivered |
2 |
SalesStatus |
Invoiced |
3 |
SalesStatus |
Canceled |
4 |
This can occasionally be useful during development and testing when extensible enums are used.
The lesson here is that we are free to use comparisons on the enums that we control, but we can't assume that a third party (ISV or Microsoft) will not change a standard enum to be extensible. This also means when referencing enum values in query ranges, we must use the SalesStatus::Backorder format and not 1. Even if the enum is not extensible, we should still use this format.