Monday, September 26, 2016

Development tutorial: Extensible base enumerations in Microsoft Dynamics AX 7

Introduction

Microsoft Dynamics AX 7 is a game changer when it comes to applying customizations required for running your specific business. Specifically, the development approach changed to focus a lot more on extending the application instead of over-layering elements to customize application flows.

Over the following months I will try to describe some of the examples of where Microsoft has ensured that the application can be extended without modifying existing AOT elements.

If there is a specific topic you would like me to cover, let me know in the comments.

Topic of today

Today I would like to start by talking about Base Enums. Enums in X++ are defined in AOT to represent a list of literals, or named constants, if you will, which then can be used in the code in a convenient way. They are stored as integers in the database.
As all of you I'm sure are already familiar with this concept, I will no go further into this.
If necessary, here's a refresher: https://msdn.microsoft.com/en-us/library/aa881702.aspx

Base enums have caused a lot of grief when writing customizations or when doing code upgrade, as you always needed to be aware of new values Microsoft might add to the list in future updates, some of the ISVs could add to the list, etc. So normally you'd have to remember to add a gap in values when adding your new enum value.
You also needed to be very careful about changing existing values as to not break the existing data in the database.

In AX 7 it is not that easy. Since there is a much more strict separation into models, with Platform and Foundation pieces being clearly isolated from Application components, customizing by overlayering base enums is not as easy, and not possible in some cases.

Example

Let's look at NumberSeqModule base enum as an example. You all are most probably familiar with this enum, since that's where you would need to add your new application module for new solutions, if you want it to be handled through the common Number sequence framework.
In AX 7 this enum is defined in the Application Platform model, meaning that it cannot have any values that are application-specific, as well as that it cannot be over-layered.

That means that the only way to go is to extend the enum.
If you are not familiar with the concept of extensions in Dynamics AX 7, you can read a good description on our wiki page: https://ax.help.dynamics.com/en/wiki/customization-overlayering-and-extensions/

In order to be able to extend a base enum, it needs to be marked as allowing extensions, which is done through the property IsExtensible.

Note that in Application Platform there are a few enums that are extensible, while the majority does not allow it. That is done on purpose, for performance reasons, based on the belief that partners would not need to extend these enums.

If that is not the case, you should let Microsoft know, so they mark the corresponding enum as extensible and release it as a hotfix.

As a result of extending an enum, a new base enum element will be created in the current model, and you will be able to add one or more element values to it.

At runtime AX will collect the values from the base definition and all extensions across all models and present the combined list of values to the user.

Note that you cannot change the properties of the base enum through extension, only add new enum values.

Difference

The major difference of an enum that has extensions vs the legacy enum is in the way how they are represented under the hood.

The extensible enums are represented in CLR as both an Enum and a Class, where each enum value is represented as a static readonly field. So accessing a specific value from the above enum, say, NumberSeqModule::Invent would under the hood look something like NumberSeqModule_Values.Invent, where Invent is of type NumberSeqModule which is an Enum. It would in turn call into AX to convert the specific named constant "Invent" to its integer enumeration value through a built-in function like the symbol2Value on DictEnum.

You'd need to add a using statement to your project, as shown below:
using Dynamics.AX.Application.ExtensibleEnumValues;
This class containing all the values would be built based on all the extensions of the corresponding enum, thus providing a generic way of handling them.

Nothing changes on the database layer, the values are still stored as integers, as today.

X++ impact

  • What this means for your every-day X++ development is that things like comparison of different enum values are out of the question. So you cannot, for example, write
if (workTable.WorkStatus <= WHSWorkStatus::InProgress)
You would now need to be more explicit, listing all the specific values that are applicable through an equality operator. (Like, Open and InProgress)
  •  This also means that you cannot rely on a certain enum value having a specific integer value, since they will be assigned per-deployment depending on all the different extensions for this enum you have deployed. So doing enum comparisons to their integer values is a Big No from now on.
if (workTable.WorkStatus == 0)
Note that both of the above have been a Best Practice for enums for a while now, so your modifications should hopefully not be impacted, if a certain enum is made extensible.
  • For extensible enums there is still an implicit conversion to and from integer type, but a warning will now be shown, to warn the developer that the enum value might be incorrect as a result of the assignment.

Development environment impact

In Visual Studio the impact is visible for any kind of metadata properties where enum values need to be specified. That includes Fixed field and Related Field Fixed when defining relations on tables and data entities, filter values on Query data source ranges, etc.

For example, when defining a Fixed field relation, you would now be able to select the enum value from a dropdown:

Setting value of an extensible enum on a Fixed field relation

Note how the full notation is used instead of what you'd do before with specifying the integer value of the corresponding value.

On query data source ranges you would instead use the short notation by just specifying one or more enum values, comma separated:

Setting value of an enum on a Query range

Conclusion

As you have seen, extensible enums is a way the platform now supports adding additional values to existing base enums without customizing these enums, which really simplifies upgrade going forward.
It however introduces certain limitations on how you use these enums in X++ code and metadata that you need to familiarize yourself with.

Let me know if you have any questions on this