Chain of Command in D365 F&O - Best Practices for X++ Code Customization
Table of Content:
What is Chain of Command in D365?
Chain of Command is the term that describes how we customize, or extend, base Microsoft code in Microsoft Dynamics 365. Microsoft’s base objects and code cannot be changed directly in D365. However, we are able to make changes to separate objects and classes that are then combined with the existing base object to form the final version.
Key points of chain of command
- An extension class is used to wrap
protected
orpublic
methods of classes, tables, data entities, and forms. - When wrapping a method, you can access
public
andprotected
methods as well asvariables
of the base class. Using a wrapper around a method and thenext
keyword creates a Chain of Command (CoC). - Wrapper methods in an extension class are required to always call
next
so that thenext
method in the chain and, finally, the original implementation is always called. This process ensures that every method in the chain factors into the result. The method must callnext()
unconditionally.
Why Can't We Modify Base Microsoft Objects?
In Microsoft Dynamics 365, Microsoft re-architected the code and made it so base objects cannot be changed. In versions 2012 and before, users could modify the base Microsoft source code directly. The problem is that anytime Microsoft released a new hotfix or new features, the new code would need to be merged in with the customized code. This was a very time consuming process.
Now in D365, since the base code cannot be modified, Microsoft can safely deploy updated versions of the base code, that contain hotfixes and new features. Unlike before, custom code will not be overridden, because all customizations must be done in separate, but related objects.
This is a huge benefit as it allows users of Microsoft Dynamics 365 to take hotfixes and new features much faster, easier and safer, than they ever could before. There is still some risk that the new version of the base code once combined with any custom code will work differently than before. Therefore testing is still needed. But this new extension model essentially makes it so no code merge is needed.
When To Use Chain of Command
In order to modify a base Microsoft object in Dynamics 365, we need to use the tools that Microsoft provides to ‘extend’ the base objects. Table and form AOT nodes can be extended using additional Extension nodes in the AOT. However, if you want to extend x++ code, you need to use either Event Handlers or Chain of Command.
While Event Handlers still have their place and are supported. As a developer, I personally find that using Chain of Command is easier to write and read.
You can use Chain Of Command for Public
and Protected
method only. You can't use it for Private
method.
Chain Of Command Basics
Microsoft has written some really good documentation on Chain Of Command. I will reference and expand of some of their examples.
Let’s look at a very basic example. Consider you have a base Microsoft class that looks like this:
class BusinessLogic1 { str doSomething(int arg) { // ... } }
As a developer, you are not allowed to change any code in the method ‘doSomething’ in D365. However, using Chain of Command you can create a new class and write code that will run before or after the base method.
[ExtensionOf(classStr(BusinessLogic1))] final class BusinessLogic1_Extension { str doSomething(int arg) { // Part 1 var s = next doSomething(arg + 4); // Part 2 return s; } }
Now, anytime the system calls the method ‘doSomething’, it will actually call any Chain of Command class containing ‘doSomething’. This will call your custom code, while also calling the base Microsoft version of the method.
This is extremely powerful as your custom code can do the following:
- Take any values passed in as parameters, and change them before they call the base Microsoft code.
- Add code that runs in addition to the base Microsoft code.
- Retrieve any return value from the base Microsoft code, and potentially change the value before returning it to the code that called this method.
Chain of Command Rules
There are a few required pieces that need to be done in order for the compiler to recognize our new class as a Chain of Command class.
- The name of the new class you create must end with the text
_Extension
. In the example above, the base Microsoft class was namedBusinessLogic1
. The name of the class we created wasBusinessLogic1_Extension
. Pro Tip: Because classes and forms and tables can be named the same thing, and each can be modified using Chain of Command, I have found it helpful to put the object type in the name of the Chain of Command class. This is not required, however, it can make your code more readable. I recommend using a format like this:<BaseObjectName>_<ObjectType>_Extenson
- The keyword
final
needs to be used in the class definition line. - The class needs to have the attribute
[ExtensionOf(classStr(<NameOfBaseClass>))]
TheclassStr
text above will change depending on the type of base objecting you are extending. I will explain more later. - Add the method definition the exact same way it appears in the base class. The wrapper method must have the same signature as the base method.
- When you augment form classes, only root-level methods can be wrapped, not methods that are defined in nested classes.
- Your code must call the base class’s method inside your extension method using the
next
keyword. Using the above example you need to have this line.var s = next doSomething(arg);
Objects That Can Use Chain Of Command
As a review, there are several different objects in Microsoft Dynamics 365 that can use Chain Of Command. I will list the most common here.
- Classes
- Tables
- Forms
- Datasource on a form.
- Data field on a form.
- Control of a form.
- COC for Form Data Field
- Data entities
Using The Right Global Function
All of the rules above apply for all uses of Chain Of Command. However, the function you use within the ‘ExtensionOf’ method above will be different based on the type of object you are extending.
While you could enter a string inside the ExtensionOf function, this would not be following best practice. The reason why we want to use a global function is because if the name of the base object ever changes name, using a global functional will cause a compile error to return. This will inform the developer that they need to make a change. Whereas if you use a string directly, you will not get a compile error, and your Chain of Command class will no longer work.
Let’s go through each type of global function we can use inside of the ExtensionOf function.
- When extending a class,
use classStr(<NameofBaseClass>)
- When extending a table, use
tableStr(<NameOfBaseTable>)
- In the case of a form, use
formStr(<NameOfBaseForm>)
- When extending a form Datasource use
formDataSourceStr(<NameOfBaseForm>,<NameOfDataSource>)
- When extending a form Data field, use
formDataFieldStr(<NameOfBaseForm>,<NameOfDataSource>,<NameOfField>)
- For a form control, use
formControlStr(<NameOfBaseForm>,<NameOfControl>)
- When extending a data entity, use
tableStr(<NameOfBaseDataEntity>)
Extend A Form Method
Do you have a method on a base form that you need extended? Or perhaps you need to set the whether a new field is visible or enabled by default. Extending the ‘init’ method on a form is a great place to add this code.
In this example, I used Chain of Command on the SalesTable form’s init method.
[ExtensionOf(formStr(SalesTable))] final class SalesTable_Form_Extension { public void init() { next init(); //customize code goes here } }