D365FO Extension Patterns
D365FO Extension Patterns
for operations
extension patterns
Prepared for
Microsoft Internal
Table of Contents
What is intrusive customization: .................................................................................................................. 4
Table Extension ............................................................................................................................................. 4
What is in scope: ....................................................................................................................................... 4
What is not in scope: ................................................................................................................................ 4
Best practice: ............................................................................................................................................ 4
Add a method to a table ........................................................................................................................... 4
Add a display or edit method to a table ................................................................................................... 5
Event handlers and delegates (hooks) ...................................................................................................... 7
Events listed inside the event node ...................................................................................................... 7
Pre and Post event handlers on public methods .................................................................................. 8
Subscribe existing delegates on table methods.................................................................................... 9
Class Extension ............................................................................................................................................ 10
What is in Scope:..................................................................................................................................... 10
What is not in scope: .............................................................................................................................. 10
Best practice: .......................................................................................................................................... 10
Extension class declarations ................................................................................................................... 10
Reason why it is marked as final ......................................................................................................... 10
Methods in extension class ................................................................................................................. 11
Add new method to existing class .......................................................................................................... 11
Instance methods................................................................................................................................ 11
Static methods .................................................................................................................................... 11
Add new state ......................................................................................................................................... 12
Instance state ...................................................................................................................................... 12
Static state .......................................................................................................................................... 12
Chain of Command ................................................................................................................................. 12
Wrapper methods must always call next............................................................................................ 12
The Hookable attribute ........................................................................................................................... 16
Final methods and the wrappable attribute ....................................................................................... 16
Method signatures .................................................................................................................................. 16
Option 1: Method overloading ........................................................................................................... 17
Option 2: Class state ........................................................................................................................... 17
Option 3: Disposable context .............................................................................................................. 18
Register a subclass for factory methods ................................................................................................. 20
Respond by using EventHandlerResult or types that implement IEventHandlerResult ......................... 21
EventHandlerResult ............................................................................................................................ 21
Accept and reject request/response scenarios .................................................................................. 21
Extend the RunBase class........................................................................................................................ 23
Limitations in class .................................................................................................................................. 27
Extension support for methods with DataMemberAttribute ............................................................. 27
Form Extension ........................................................................................................................................... 28
What is in scope: ................................................................................................................................. 28
What is not in scope: .......................................................................................................................... 28
Best practice: ...................................................................................................................................... 28
Form method .......................................................................................................................................... 29
Through chain of command ................................................................................................................ 29
Through pre-post event handlers ....................................................................................................... 29
Form Event .............................................................................................................................................. 31
Form Datasource method ....................................................................................................................... 32
Form Datasource field method ............................................................................................................... 33
Form Datasource Event........................................................................................................................... 36
Form control Event ................................................................................................................................. 38
Enum extensions ......................................................................................................................................... 40
EDT extensions ............................................................................................................................................ 41
Report extensions: ...................................................................................................................................... 41
Frequently used patter in upgrade: ............................................................................................................ 43
What is intrusive customization:
1. Don't change a method signature.
2. Don't change requirements for implementers of interfaces and table maps.
3. Don't change requirements for classes that are derived from abstract classes.
4. Don't reduce access modifiers for types or members.
5. Don't change constraints that are defined on a table or a data entity
Table Extension
What is in scope:
1. Adding new fields
2. Field groups
3. Indexes
4. Mapping
5. Relation
6. Add new fields to existing field groups
7. Change the label of a table field
8. Change the Created By, Created Date Time, Modified By, Modified Date Time (except inherited
table), country region codes properties etc.
9. Adding new methods by COC
Best practice:
a) Do not name the extension just <Element that is being extended>.Extension. For example, an
extension class that augments the custGroup table must not be named custGroup .Extension,
because the risk of conflicts is too high.
b) Fields, indexes, relations, and other metadata elements on extension elements must have a
name that is unique across both the element that is being extended and other extension
elements. Therefore, these metadata nodes must include a term, abbreviation, or prefix that
minimizes the risk of conflicts across models.
Requirement is to add a display method in the standard table, it can be achieved either by creating a table
extension or static class.
There are already plenty of events (hooks) we can subscribe in AX and extend the existing functionality
without overlayering or overriding the function we need to extend. You can find those under the “Events”
node of the application objects. Subscription is as easy as right clicking and selecting “Copy event handler
method”, then you need to paste it into an eventHandlers class and write your own logic inside
Note - that the object methods (like insert, update, init, write.) we used to override in the AX 2012 now
corresponds to two different events, as ‘Deleting’ and ‘Deleted’, and we no more call super() in our event
handler methods.
On all events you subscribe you also have a parameter that contains an “event args” object for the
corresponding event. However, this parameter is not always the event args class you exactly need. They are
generally down casted to their most common base class (DataEventArgs for data related events or
FormEventArgs for events on the form root) and you need to cast it back to the args class you need.
Example
There are a lot of event handlers with down casted parameters like this one and it is impossible to list all of
them here. Please refer below reference link –
Reference - https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-
itpro/extensibility/customization-overlayering-extensions#table-extensions
Pre and Post event handlers on public methods
Beyond these events listed inside the Events node, there are Pre and Post event handlers which you can
subscribe to existing public methods and run your own logic before or after the original method is executed.
And it is possible to change the return type and access the method parameters using the XppPrePostArgs
parameter like the example below
Note – pre and post event handlers are not type safe. So, chain of command is recommended way for these
methods.
Subscribe existing delegates on table methods
There are already many delegate functions in table events section published by Microsoft.
Reference - https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-
itpro/extensibility/respond-event-handler-result
Class Extension
What is in Scope:
1. Public methods
2. Protected methods
3. Only methods that are defined in regular classes can be wrapped
4. Static methods
5. Instance state
6. Static State
7. Extension classes can wrap default parameters of method. However, the method signature in
the wrapper method must not include the default value of the parameter.
Best practice:
a) DO NOT name the extension of class simply as _MYExtension. Element name should be append
with _Extension. Example: MyClass_Extension or <classname>+ <model abbreviation>_extension
b) DO start the name of the extension class with the name of the type being augmented, and end it
with the term _Extension.
Extension classes are classes that are adorned with the ExtensionOf attribute and that also have a name that
has the _Extension suffix. (This restriction on the naming might be removed later.) The name of the
extension class is otherwise unimportant. The class augments the artifact that is specified in the ExtensionOf
attribute, as shown in the following example.
The following example shows how to call the method in the model.
Static methods
Methods that are defined as public and static in the extension class are available as static methods on the
artifact that is augmented.
The following example shows how to call the method in the model.
Add new state
In addition to providing static and instance methods to an artifact, you can add instance state and static
state.
Instance state
Instance state, which is state that pertains to an instance of an artifact, can be specified on extension classes.
The following example defines a state that is named state.
Static state
Static state applies to the type instead of an instance of the type. The following example shows a static
extension state. Static constructors are the parameter-less static methods that are named typenew. Static
constructors can be defined on extension classes. It's guaranteed that the runtime system will call the
constructor before the first reference to the extension type.
Chain of Command
You can now wrap logic around methods that are defined in the base class that you're augmenting. You can
extend the logic of public and protected methods without having to use event handlers. When you wrap a
method, you can also access public and protected methods, and variables of the base class. it required use of
the next keyword create a Chain of Command (CoC) for the method.
In the current implementation of this restriction, the call to next must be in the first-level statements in the
method body.
Here are some important rules:
Now we can add pre and post functionality to extensible methods in a much easier and readable manner
than the previously used event handlers, also we are now able to access protected methods and variables
directly in the extended class without problems. Now let’s check how it works with an example.
Example of CoC
Create two classes, one is the base class of the other and add a method we want to extend called ‘testMe’.
Add Infolog calls inside the method to track the execution
Create a new model, add reference for the class model we created above, and add an extension class for our
child class.
The method we added here is the new chain of command definition. We use exactly the same notation as the
original method we are extending and add a mandatory “next” keyword to the call which will wrap our
extension code around the extended method. This next keyword separates the pre and post parts of our
extension and mandatory to be called inside the chain of command method. When you call next(), it checks
for other extensions in the queue and runs them in random order, lastly running the base code.
The advantage of chain of command is you can share the same method variables in the pre and post
(before/after next() call) or share the same tts block inside your COC method.
Example of CoC with return value
COC also supports return value and parameter modification of the extended method in a much more
readable manner. Extending a method with return value and parameters in COC is like below
Example
The Hookable attribute
If a method is explicitly marked as [Hookable(false)], the method can't subscribe to pre-events and post-
events by extenders. In the following example, anyMethod can't be subscribe to pre-events and post-events
by extenders in a class that augments anyClass1.
In the following example, the doSomething method is explicitly marked as non-wrappable, even though it's a
public method. The doSomethingElse method is explicitly marked as wrappable, even though it's a final
method.
Method signatures
Method signatures are not extensible – and will not be. Extensible parameters would be intrusive – in so
many ways
Description
X++ doesn't support overloading methods – but you can mimic the behavior by creating a new method (with
new name) in an extension class. The extension method can take additional parameters and call the original
method; with your logic before and after calling the original method.
Example
Here is an example of "overloading" the insert method on CustTable. Notice the first 3 parameters are
identical to the parameters on the original insert() method.
Here is an example of the consuming code. The standard implementation of CustTable.Insert() calls
DirPartyTable::createNew() – the example uses Chain-of-command to wrap the createNew method, and then
accesses the context to get the information.
Caution
Transferring state from one arbitrary place to another using a global variable (which the context class is) can
lead to future logical errors – that can be very hard to detect and fix. Recommendation would be to limit the
usage to situations where the scope and consequences are manageable.
Register a subclass for factory methods
Reference
https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/extensibility/register-
subclass-factory-methods
Respond by using EventHandlerResult or types that implement IEventHandlerResult
EventHandlerResult
Delegate methods and delegate handler methods can be declared to support a request/response scenario,
where the delegate calling logic requests the subscribers to provide a response. To support this scenario the
EventHandlerResult class is most often passed as a parameter, and the delegate handler methods provide
their result using one of the result methods on the class. However, the EventHandlerResult class can only
contain a single result.
The EventHandlerResult class has an additional static constructor which ensures that the logic fails if more
than one subscriber provides a result. The new constructor is named newSingleResponse. When instantiating
an EventHandlerResult object using this method, the framework will throw an exception as soon as a second
delegate handler method attempts to provide a result.
Guidelines
In general, the logic that is implemented in the delegate handler method should contain a condition
that verifies that the subscribing logic is responsible for providing a response. It should also include
logic to provide the response in the form of a result.
When the delegate handler method must provide the response to an EventHandlerResult object
parameter, the subscribing logic might also contain logic to calculate or retrieve the result.
When the condition and the response logic are implemented, the calculation of the result must occur
only when the condition is evaluated to true.
All the subscribing delegate handler methods are run when a delegate is called. Therefore, you should
make sure that the overhead of running your method is as low as possible when the method isn't
responsible for providing a response. Therefore, make sure that the condition is evaluated to false as
quickly as possible when your delegate handler method isn't responsible for providing a result.
When using the EventHandlerAcceptResult class, the delegate handler method can only respond by calling
the accept method.
Method
Delegate
Subscriber method
When using the EventHandlerRejectResult class, only the reject method can be called.
The following example shows a delegate handler method that responds by using an
EventHandlerRejectResult object. To respond by using an EventHandlerRejectResult object, you can call the
reject method or the checkFailed extension method. If you use the checkFailed method, you can add a
warning message to the Infolog. Internally, the checkFailed method calls the reject method.
Method
Delegate
Subscriber
Reference
https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/extensibility/respond-
event-handler-result
Extend the RunBase class
How to extend the information shown on a RunBase-based dialog, and how to handle that information
once the user enters the necessary data. Also, how to preserve the user entered data, so that next time
the dialog is opened, it contains the last entries already populated. This is the typical pattern used across
all AX forms and is internally based on the SysLastValue table.
Suppose we want to extend the SysUserLogCleanup class. Out-of-the-box this class is deleting records
from the SysUserLog table. Let's imagine we want to archive these records to a different table before
they are deleted. The SysUserLogCleanup class is a RunBase class. The RunBase class has a dialog box,
where the user is prompted for parameters before the class is run. For this example, we will add a toggle
button control to the dialog box, get the value of the control, act on the value in the run method, and
make sure that the value is serialized via the pack and unpack methods. Serialization helps guarantee
that the user’s last selection is presented again if the dialog box is reopened. It also helps guarantee that
the settings are applied if the class is run in the background.
Reference
https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/extensibility/extend-
runbase-class
https://blogs.msdn.microsoft.com/mfp/2017/01/31/extending-class-state/
Limitations in class
Extension support for methods with DataMemberAttribute
Dave Froslie - DataMemberAttribute support is a platform request that has been made by several
partners. The request has some risk associated with it, and the best case is for it to be in PU19 which is
several weeks out - https://msdyneng.visualstudio.com/FinOps/_workitems/edit/199219.
Form Extension
What is in scope:
1. Add a new control.
2. Enable or disable a control.
3. Change the text or label property of a control.
4. Change a control's visibility.
5. Change a form's help text.
6. Change a form's caption.
7. Add a new data source.
8. Add a form part.
9. Implement the code in class as COC
a) Form methods
b) Form data source method (formdatasourcestr)
c) Form data source field method (formdatafieldstr)
d) Form data source control methods (formcontrolstr)
Best practice:
a) Do not name the extension just <Element that is being extended>.Extension. or <Element that
is being extended>.Extension1… For example, an extension class that augments the
SalesParameters table must not be named SalesParameters.Extension, because the risk of
conflicts is too high and also it would be carry the upgrade cost.
Form method
Example –
Form name – BOMRouteCopyDialog
Customization: CloseOk():
Open the “Methods” node and copy an event handler for the init()method from here.
Form Event
Extending form method example
Now we have another option to extend the init method on the form. Option one is to open the “Events” node
and hit “Copy event handler method” on the “OnInitialized” event. OnInitialized is a post
event, OnInitializing the opposite, a pre-event.
The difference is minimal, in both cases we will call args().record() from the FormRun object. When using the
post handler on the init method, we will have to get this FormRun object first, not a big thing, we have just to
call the getThis() method on the provided arguments of the handler.
Result – If we select a customer and hit “Non conformances” from the “SELL” tab on the CustTable form, we
will see the following Infolog.
Form Datasource method
Now PU20 onwards, you can wrapped the form data source methods, data source field methods and control
methods.
Note - "What about our custom-made methods, we can’t add those to an extension class if they are under a
datasource."
Answer - yes, you can add your custom methods (PU20) to DS on class extension. However, if there is any
problem with methods implementation please moved the method to form level with some tricks. So, the
pipeline is
Move your custom methods from DS level to Form level
Move your custom form-level code to Extension class
Create eventHandlers for required events
Call your custom method using formhasmethod()
Explained design below in Form Datasource field method
Form Datasource field method
Common customization task to override standard form data source field methods like jumpRef(), modified()
or validate().
Recommended approach in AX 7 is to avoid overlaying and use extensions as much as possible. To achieve
this new event were introduced. However, on form data source field level we have only 3 available events:
For example, we want to add custom jumpRef() and validate() methods to itemId field on sales order
form. First, we will create class to handle method overrides.
Then we will create new event handler class and subscribe to OnInitialized event of SalesLine data source.
Using this approach, you can override any data source field method.
Note – “Known” issue - doesn’t work for lookup method on datasource field.
Form Datasource Event
Form control Event
Below is the list of the Form > Design > control level event handler methods can be written - differs basing
on the data type the control (For Combo box/enum type the event will be different).
Example 1 –
Example 2 –
Let say in our example we will write the event handler for the form data source > Design > Control level >
Onlookup methods in the "LedgerJournalTransCustPaym" standard form.
Open the Form > LedgerJournalTransCustPaym > Data Source (LedgerJournalTrans) > Design >
Control level > Property_AgreementId is the custom field added to LedgerJournalTrans. Expand the
Events (expand the events node). As shown in the above the screen shot.
Right click on the Onlookup> copy the event handler method. Below is the screenshot.
Enum extensions
You can extend any Enum that is marked extensible (IsExtensible=True) and
UseEnumValue = No
By extending an Enum, you can add new Enum values to it. It is important to keep the following in mind when
dealing with extensible Enums:
1. You cannot have X++ logic that depends on the integer value of Enum values (For example. If
(Enum1.v1 > Enum1.v2) ... is not supported for extensible enums)
2. When Enum values of extensible Enums are synchronized into the database:
Integer values that belong to the baseline enum are deterministic. They come from the
metadata.
Integer values that are an extension are generated during the synchronization process and
are not deterministic.
Best practice:
If your enum has many elements, such as more than 100, consider redesigning the solution instead of making
the enum extensible. If the enum is extensible, then adding more elements in the future might break
customer’s combined solution as the addition might exceed the limit.
EDT extensions
You can extend an EDT element in order to modify any of the following properties:
Form help
Label
String size
Help text
Note: For derived EDTs, string size can't be changed by an extension, because the IS-A relationship between
the EDTs will be broken.
Report extensions:
https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-
itpro/analytics/custom-designs-business-docs
Note: For maintainability purpose, maximum print management report is getting call by
getDefaultReportFormat instead of report name.
Add a delegate handler method to start to use your custom report. Extend
the getDefaultReportFormatDelegate method in your own custom class as below
Now you can extends the exiting controller class and use below code.
Option 1:
Note: if you faced an issue that the new report design was not being displayed even after
creating the above extension class (Option 1) and referring to the new report design, so you
can resolved this issue by adding the below outputReport method:
Frequently used patter in upgrade:
Example:
Example:
Correct approach:
Limitation: We cannot invoke one of these methods in a chain of command wrapper somewhere else
because only the base code would be executed and COC code won’t be execute.
Example:
Pattern 5: How to cancel super call in from data source method before executing the standard validation.
Now you can call the COC for data source method and return true value.
Pattern 6:
Pattern 7: If method is predefine in table, you can write COC.
Pattern 8: form data source control level evend hndler
However, jumpref is not displaying under events tree hierarchy, we can write event for jumpref as discussed
in above.
Pattern 9: How to modify the data source field value in extension.
Pattern 10: how to access the members from data source methods.