Skip to main content

Hierarchy conflicts in AX 7 during migration from other AX versions and it's resolution - Delegates and Handlers

Delegates and Handlers in AX 7(Hierarchy conflicts resolution)


Hi,

We recently did a migration of our AX 2012 solution to AX 7, and while doing so one of the major challenge that we have faced are hierarchy conflicts. I'll be discussing here on the same.

First of all we need to know that what is a hierarchy conflict.

Coming to the AX 7 architecture, Application stack is divide into three basic packages. These are-

1: Application Platform
2: Application Foundation 
3: Application Suite


As shown in the above image, Application platform is the base package which contains Runtime and data access , work flow and services etc. and on top of it we have application foundation which contains organisation model, number sequence and GAB etc. and at the top we have Application Suite and Fleet Management. To understand the concept of these packages in depth you can follow the below link-


All three models follows a hierarchy. Application platform will be having access to elements from it's own package. Elements from application platform can't refer to elements from packages on top of it. In the same way Application foundation can have access to it's own elements and elements from Application Platform, but not from Application Suite. Application suite can access elements from both lower models and it's own elements.

Hierarchy Conflict:

Now the question comes what is a hierarchy conflict. So lets consider a scenario where an element which lies in Application Foundation is trying to use properties of an objects which is from application suite.This is an example of hierarchy conflict because Application foundation can access it's own objects and elements which belongs to model below i.e. Application Platform. It can't access elements of higher models.

Let's understand it with a simple example:
Lets consider two classes "SalesPrice" and "Profit".

1: SalesPrices (Application Foundation)
2: Discount (Application Suite)

public class SalesPriceClass
{
    real cost;
    real profit;

    public real calculateSalesPrice()
    {
        real salesPrice;
        salesPrice = this.cost + this.profit;
        return salesPrice;
    }
}

This SalesPrice is not the actual salesPrice. Consider a discount scenario which needs to be deducted form the current salesPrice to get the actual salePrice. And this discount belongs to a different class.
Other class is "Discount"class which belongs to Application suite;

public class Discount
{
    static real discountValue;


Now it's not possible in the SalesPriceClass to get the discountValue and get the actual salesPrice by subtracting from the salesPrice beacuse SalesPriceClass (ApplicationFoundation) can't access the elements of Discount (Application Suite).

Now how to resolve such a scenario (hierarchy conflict).

The best method to resolve such a conflict is to use a Delegate.

How to use Delegates and Handlers

A delegate declaration must have three things:
  • The delegate keyword
  • Type void
  • Empty method
Delegate methods serve as a means for defining a contract between the delegate instance and the delegate handler. A delegate takes no action itself. This is enforced by having a void type and having no code in the method.

delegate void applyDiscount_delegate(real _cost, EventHandlerResult _res) {}

Adding the SubscribesTo keyword to a method will create a static delegate handler. SubscribesTo requires the class name of the delegate, and the string name of the delegate method.

[SubscribeTo(classStr(SalesPriceClass), delegateStr(SalesPriceClass, applyDiscount_Delegate))]
public static void onApplyDiscount_Delegate(real _cost, EventHandlerResult _res)
{
    real actualCost = _cost - discountValue;
    _res.result(actualCost);
}

In order for a delegate to be properly handled, the delegate method declaration, the delegate instance, and the delegate handler must have the same method signature.

Due to the fact that delegates do not have a return value, an EventHandlerResult is passed as a parameter to provide access to the needed result value after the delegate has returned.

Our Example -

In our scenario, we will modify SalesPriceClass and add a new delegate to the class.

SalesPriceClass in ApplicationFoundation Model-

public class SalesPriceClass
{
    real cost;
    real profit;

    //Adding a new delegate
    delegate void applyDiscount_Delegate(real _cost, EventHandlerResult _res){}

    public real calculateSalesPrice()
    {
        real salesPrice;
        EventHandlerResult result = new EventHandlerResult();
        //salesPrice = this.cost + this.profit;
        this.applyDiscount_Delegate(cost, result);
        salesPrice = result.result() + profit;
        return salesPrice;
    }
}

Discount Class in ApplicationSuite Model-

public class Discount
{
    static real discountValue;

    [SubscribeTo(classStr(SalesPriceClass), delegateStr(SalesPriceClass, applyDiscount_Delegate))]
    public static void onApplyDiscount_Delegate(real _cost, EventHandlerResult _res)
    {
        real actualCost = _cost - discountValue;
        _res.result(actualCost);
    }
}

Important Note:
Concept of Delegates is only applicable to methods. 

Find delegates and handlers

There are three key ways to find delegates and handlers
  • Metadata search
  • Class references
  • SubscribesTo references
The Metadata search tool, described on the Metadata search in visual studio page, is the best way to find either delegates or their handlers. In Visual Studio, go to Dynamics AX > Metadata Search to open the metadata search tool.



In the search field, type “code:<delegate name>” which will restrict the search to code and find any use of the delegate name, returning both the delegate and handler. Metadata search will search the entire code base and may take some time to complete, but will return any use of the search term in code.



Methods two and three can be used in parallel to the metadata search. The class where a delegate is defined can also serve as a means to narrow down the search for a delegate or handler. The SubscribesTo keyword requires the class name where the delegate was defined. Visual Studio’s find references (right-click the class name > find references) will return a list of files that reference the class. This list will include both the class definition where the delegate is declared and the handler referencing the class. Finding class references is not a perfect method and will require some manual searching through class references. However, it produces a smaller subset of files and can be faster than a metadata search.  

Comments

Popular posts from this blog

Get account Structure ranges set in AX Ledger

How to pass Account Structure ranges set in AX Ledger to an external system. Recently I have got a requirement where I have to populate same set of main account and financial dimension values to an external system using integration off course, which I have set in AX Ledger. The difficulty in this scenario was, how to get the valid set of main account and financial dimension values from AX.  Because AX gives us 'Segmented Entry Controls' where we just have to use the field as a segmented entry control and valid values will populate according to the Account Structure set in Ledger.  I have created a service class which extends "DimensionValueService" public class CMCDimensionValueService extends DimensionValueService {     // Insert list of valid main accounts to 'DimensionValueContract'     private void insertMainAccountValueLocalList(List                       _dimensionValueList,                                                  Dime

Mass cheque printing in AX7/Dynamics 365 for finance & operation

AX7/Dynamics 365 for finance & operation: How to avoid user interaction while printing multiple cheques(Mass cheque printing) You all might be aware of the ‘Generate Payment’ functionality available in Dynamics AX. This function we use to generate/print cheques. The process is to create one payment journal, add few transactions with mode of payment as ‘Cheque’ then go for ‘Generate Payment’ option available in the drop down of ‘Function’ menu item button. Process is specific to print one cheque at a time for a single journal. Recently I came across a requirement where I had a customized form which contains multiple journal records and I have to add the ‘Generate Payment’ button on the form to allow user to print the checks for journals, and also allow user to select multiple journal records at the same time to print cheques and avoid user interaction with the system for each cheque. The major challenge here was, how to handle user interactions everytime to print multiple