Friday 3 March 2017

AX7 – Events and Subscriptions

Why we should use events

If it’s not clear to you yet what the actual benefit of eventing is I would like to note that in AX 2012 already the first and most important one is upgrade complexity and in the end upgrade cost. Code modifications do need to be merged potentially, event subscriptions don’t. I know that saying so simplifies things a little…
Secondly there were some changes made in the structure of the application that make a transition to eventing mandatory in some cases. I’ll cover this topic later with a separate blog post (or likely several) but for reference the keyword here is Packages.

Types of events

There are three types of “events” you can subscribe to in AX7:
  • Standard events
  • Delegates
  • Pre-/Post-Events

Standard events

AX7 Table Events
The biggest change here comes with the Events nodes you find in the designer for a lot of object types. Microsoft introduced loads of standard events for the relevant phases of execution. As an example in AX 2012 you needed to modify or at least pre- or post-event the update() method of a table to extend its behavior. Both still is possible but in addition to that there are two events you can subscribe to, onUpdating() and onUpdated().

Delegates

Delegates were there before and still are but subscribing to them changed. What changed and is said to be an ongoing process in the future is that Microsoft adds delegates in certain places to enable us to extend the behavior of things (without having to modify standard code). An random example would be that in class WmsLocationTreeBase there’s a delegate for the change of the tree so you can subscribe and react to that event:
delegate void treeChanged()
{
}

Pre-/Post-Events

You can still add pre- and post-events to any method. Just the way to do it changed.

Subscribe to events and find subscriptions

AX7 Table Events Copy Find Event Handler
To create a new subscription in all three cases you can simply right-click the event source, standard event / method / delegate, in the designer of the object and click Copy event handler method. This copies the code for a valid and well-formed subscription method to your clipboard. You only need to create a new event handler class (or use an existing one, of course) and paste the code there which then looks like this (for a table named SomeTable’s event onUpdated()):
[DataEventHandler(tableStr(SomeTable), DataEventType::Updated)]
public static void SomeTable_onUpdated(Common sender, DataEventArgs e)
{
}
As you can see there are attributes and parameters; those differ between event types, of course.
To find existing event subscriptions you can simply right-click the publisher in question and Find event handlers to display all the subscribers in the Find Symbol Results area immediately.

Pitfalls

Be aware of the execution order! Getting back to the example there’s a difference betweenonUpdated() and the post-event to the update() method. Let me explain it with a little example. Having an update() method like this
public void update()
{
method1();
super();
method2();
}
that has a pre-event as well as a post-event has the following execution order:
  1. Pre-event subscriptions
  2. method1();
  3. Subscriptions to onUpdating()
  4. super() call
  5. Subscriptions to onUpdated()
  6. method2();
  7. Post-event subscriptions

AX7 – Events and Subscriptions

Why we should use events

If it’s not clear to you yet what the actual benefit of eventing is I would like to note that in AX 2012 already the first and most important one is upgrade complexity and in the end upgrade cost. Code modifications do need to be merged potentially, event subscriptions don’t. I know that saying so simplifies things a little…
Secondly there were some changes made in the structure of the application that make a transition to eventing mandatory in some cases. I’ll cover this topic later with a separate blog post (or likely several) but for reference the keyword here is Packages.

Types of events

There are three types of “events” you can subscribe to in AX7:
  • Standard events
  • Delegates
  • Pre-/Post-Events

Standard events

AX7 Table Events
The biggest change here comes with the Events nodes you find in the designer for a lot of object types. Microsoft introduced loads of standard events for the relevant phases of execution. As an example in AX 2012 you needed to modify or at least pre- or post-event the update() method of a table to extend its behavior. Both still is possible but in addition to that there are two events you can subscribe to, onUpdating() and onUpdated().

Delegates

Delegates were there before and still are but subscribing to them changed. What changed and is said to be an ongoing process in the future is that Microsoft adds delegates in certain places to enable us to extend the behavior of things (without having to modify standard code). An random example would be that in class WmsLocationTreeBase there’s a delegate for the change of the tree so you can subscribe and react to that event:
delegate void treeChanged()
{
}

Pre-/Post-Events

You can still add pre- and post-events to any method. Just the way to do it changed.

Subscribe to events and find subscriptions

AX7 Table Events Copy Find Event Handler
To create a new subscription in all three cases you can simply right-click the event source, standard event / method / delegate, in the designer of the object and click Copy event handler method. This copies the code for a valid and well-formed subscription method to your clipboard. You only need to create a new event handler class (or use an existing one, of course) and paste the code there which then looks like this (for a table named SomeTable’s event onUpdated()):
[DataEventHandler(tableStr(SomeTable), DataEventType::Updated)]
public static void SomeTable_onUpdated(Common sender, DataEventArgs e)
{
}
As you can see there are attributes and parameters; those differ between event types, of course.
To find existing event subscriptions you can simply right-click the publisher in question and Find event handlers to display all the subscribers in the Find Symbol Results area immediately.

Pitfalls

Be aware of the execution order! Getting back to the example there’s a difference betweenonUpdated() and the post-event to the update() method. Let me explain it with a little example. Having an update() method like this
public void update()
{
    method1();
    super();
    method2();
}
that has a pre-event as well as a post-event has the following execution order:
  1. Pre-event subscriptions
  2. method1();
  3. Subscriptions to onUpdating()
  4. super() call
  5. Subscriptions to onUpdated()
  6. method2();
  7. Post-event subscriptions

Wednesday 18 January 2017

Change Tracking in AX2012 R3

In AX 2012 R3, Microsoft built a small project (a couple of classes and tables) to expose the SQL Server Change Tracking to AX. In this blog I’m going to walk you through the process of exploiting this feature.
In previous versions of AX2012, Microsoft used to track changes in the the Retail module by creating handlers in the insert, update and delete methods for the tables that need tracking.
In R3, the whole method used for tracking has been changed in the Retail module. AX now uses the power of Microsoft SQL Server Change Tracking to track the changes in AX tables. We can use the same feature for other things within AX, an obvious example would be for building exports that only send changed data. Using Change Tracking means less coding (if no coding at all in some areas). Moreover, this will significantly improve performance as well.
In the following examples I added four jobs to enable change tracking for any change in the products (inventTable and any child table related to it – e.g. InventTableModule). Afterwards, I want to be able to query the tables for the changes (insert and update) that occurred in the inventTable table:

Job 1:

Firstly, to enable tracking on a table, the tracking should be enabled at two levels:
1- The whole database
2- On each table that the user wishes to track.
The following job will enable tracking on the whole database to start with. Then the job will enable the tracking for a query which in return will enable tracking for all the tables under this table.
server static void aaChangeTracking_1_EnableTracking(Args _args)
{
Query query;
AifChangeTracking changeTracking;
AifChangeTrackingTable ctTable;
;
 
//Need to be run on server side
new AifChangeTrackingPermission().assert();
 
info(strFmt("%1", AifChangeTrackingConfiguration::isChangeTrackingEnabled()));
 
// Run this first to globally enable changeTracking
//This should be run once
AifChangeTrackingConfiguration::enableChangeTracking(true);
 
// Here we are loading the query that the webservice uses (I took a wild guess to figure this out)
query = new Query("AxdItem");
AifChangeTrackingConfiguration::enableChangeTrackingForQuery("TestScope1", query);
 
changeTracking = AifChangeTracking::construct(query, "TestScope1", AifChangeTrackingType::SqlChangeTracking);
 
// Did we enable change tracking?
info(changeTracking.isChangeTrackingEnabled()? "true" : "false");
 
// did we enable change tracking for this specific query?
info(changeTracking.isChangeTrackingEnabledForQuery()? "q:true" : "q:false");
 
// redundant check... but anyways its checking if the specific table has change tracking enabled
info(changeTracking.isChangeTrackingEnabledForTable(tableNum(InventTable))? "t:true" : "t:false");
 
// Revert the permission assert
CodeAccessPermission::revertAssert();
}
In the above code, permissions need to be set and reverted in the two lines below:
new AifChangeTrackingPermission().assert();
// Revert the permission assert
CodeAccessPermission::revertAssert();

Then the system will enable change tracking by using the enableChangeTracking method. If the value passed to the method was false, then the system will disable the change tracking on the whole database.
To enable the tracking for a query, use the method “enableChangeTrackingForQuery” and pass it the query defined in the previous line. In our case, it’s the AxdItem query that is for the products.
One important thing to mention here is that this job will only run on server side. So, the easiest way to resolve this is to create an action menu item that will run on the server side and point it to this job. Normally, of course, you would place the code into a server side class; we’re using a job here just for convenience while demonstrating how to build the code.
Another thing to mention is that by default the retention period (the period SQL will keep the change tracking data) is two days. This can be increased and decreased based on the user’s preference.

Job 2:

After enabling the tracking in both the database and the tables that we need to track, we need to link the child tables to the parent table. This means that if a change occurred (insert or update) in the child table then the parent table should be touched.
E.g. In our example, I need to make sure that whenever a record in the InventTableModule has been created or updated, the inventTable is to be set as touched. This will help because I can enquire on changes later only on the parent table (e.g. inventTable) rather than checking the changes in all tables related to the inventTable. This will be done by creating triggers in the child tables to touch the parent tables. AX made this a simple process, you just need to define a query that includes all the tables needed then simple pass this to the createTouchTriggersForQuery method:
server static void aaChangeTracking_2_CreateTriggers(Args _args)
{
Query query;
AifChangeTrackingTable ctTable;
;
 
//Need to be run on server side
new AifChangeTrackingPermission().assert();
 
query = new Query("AxdItem");
 
AifChangeTrackingConfiguration::createTouchTriggersForQuery("TestScope1", AifChangeTrackingTriggerType::AfterInsert, query);
AifChangeTrackingConfiguration::createTouchTriggersForQuery("TestScope1", AifChangeTrackingTriggerType::AfterUpdate, query);
 
// Revert the permission assert
CodeAccessPermission::revertAssert();
}

Job 3:

Tracking the versions will be saved in a table in AX called AifSQLCTVersion. The following code will update this version control table:
static void aaChangeTracking_3_UpdateTrackingVersion(Args _args)
{
container conVersion;
;
 
//To record the newest version
//There is a standard AX batch job that does this
AifSqlCtChangeTracking::recordCurrentVersion();
}
This can be run as a batch job by calling the following batch job: AifChangeTrackingVersionUpdateJob
Job 4:
To find the changes, I created a batch job that will define a query for the inventTable only. Remember in job 2 we created triggers for the child tables to touch the inventTable. So, in this case we don’t need to check the changes in the child tables (e.g. invnetTableModule). we only need to call the inventTable.
In the following example, the user needsto pass the “getChanges” method two parameters:
1- AifChangeTrackingTable temp table (called it “ctTable” – the system will populate this table with the changes and the versions for the changes. it will also populate the recids for the records changed)
2- utcDateTime (This will be the change needed to be tracked since which dateTime)
server static void aaChangeTracking_4_GetChangedTable(Args _args)
{
Query _query;
QueryBuildDataSource _qbds;
AifChangeTracking _changeTracking;
utcDateTime _dateTimeYesterday;
AifChangeTrackingTable ctTable;
;
 
//Change the date based on what is needed
_dateTimeYesterday = DateTimeUtil::addDays(DateTimeUtil::getSystemDateTime(), -2);
 
new AifChangeTrackingPermission().assert();
 
_query = new Query();
_qbds = _query.addDataSource(tableNum(InventTable));
 
_changeTracking = AifChangeTracking::construct(_query);
 
_changeTracking.getChanges(_dateTimeYesterday, ctTable);
 
while select ctTable
{
info(strFmt("%1", ctTable.KeyField_RecId));
}
 
CodeAccessPermission::revertAssert();
}
And that’s it! The query returns the list of items that have been updated since the specified date and you can use the results of the query in whatever way you need.

Change Tracking in AX2012 R3

In AX 2012 R3, Microsoft built a small project (a couple of classes and tables) to expose the SQL Server Change Tracking to AX. In this blog I’m going to walk you through the process of exploiting this feature.
In previous versions of AX2012, Microsoft used to track changes in the the Retail module by creating handlers in the insert, update and delete methods for the tables that need tracking.
In R3, the whole method used for tracking has been changed in the Retail module. AX now uses the power of Microsoft SQL Server Change Tracking to track the changes in AX tables. We can use the same feature for other things within AX, an obvious example would be for building exports that only send changed data. Using Change Tracking means less coding (if no coding at all in some areas). Moreover, this will significantly improve performance as well.
In the following examples I added four jobs to enable change tracking for any change in the products (inventTable and any child table related to it – e.g. InventTableModule). Afterwards, I want to be able to query the tables for the changes (insert and update) that occurred in the inventTable table:

Job 1:

Firstly, to enable tracking on a table, the tracking should be enabled at two levels:
1- The whole database
2- On each table that the user wishes to track.
The following job will enable tracking on the whole database to start with. Then the job will enable the tracking for a query which in return will enable tracking for all the tables under this table.
server static void aaChangeTracking_1_EnableTracking(Args _args)
{
    Query                   query;
    AifChangeTracking       changeTracking;
    AifChangeTrackingTable  ctTable;
    ;
 
    //Need to be run on server side
    new AifChangeTrackingPermission().assert();
 
    info(strFmt("%1", AifChangeTrackingConfiguration::isChangeTrackingEnabled()));
 
    // Run this first to globally enable changeTracking
    //This should be run once
    AifChangeTrackingConfiguration::enableChangeTracking(true);
 
    // Here we are loading the query that the webservice uses (I took a wild guess to figure this out)
    query = new Query("AxdItem");
    AifChangeTrackingConfiguration::enableChangeTrackingForQuery("TestScope1", query);
 
    changeTracking = AifChangeTracking::construct(query, "TestScope1", AifChangeTrackingType::SqlChangeTracking);
 
    // Did we enable change tracking?
    info(changeTracking.isChangeTrackingEnabled()? "true" : "false");
 
    // did we enable change tracking for this specific query?
    info(changeTracking.isChangeTrackingEnabledForQuery()? "q:true" : "q:false");
 
    // redundant check... but anyways its checking if the specific table has change tracking enabled
    info(changeTracking.isChangeTrackingEnabledForTable(tableNum(InventTable))? "t:true" : "t:false");
 
    // Revert the permission assert
    CodeAccessPermission::revertAssert();
}
In the above code, permissions need to be set and reverted in the two lines below:
new AifChangeTrackingPermission().assert();
// Revert the permission assert
CodeAccessPermission::revertAssert();

Then the system will enable change tracking by using the enableChangeTracking method. If the value passed to the method was false, then the system will disable the change tracking on the whole database.
To enable the tracking for a query, use the method “enableChangeTrackingForQuery” and pass it the query defined in the previous line. In our case, it’s the AxdItem query that is for the products.
One important thing to mention here is that this job will only run on server side. So, the easiest way to resolve this is to create an action menu item that will run on the server side and point it to this job. Normally, of course, you would place the code into a server side class; we’re using a job here just for convenience while demonstrating how to build the code.
Another thing to mention is that by default the retention period (the period SQL will keep the change tracking data) is two days. This can be increased and decreased based on the user’s preference.

Job 2:

After enabling the tracking in both the database and the tables that we need to track, we need to link the child tables to the parent table. This means that if a change occurred (insert or update) in the child table then the parent table should be touched.
E.g. In our example, I need to make sure that whenever a record in the InventTableModule has been created or updated, the inventTable is to be set as touched. This will help because I can enquire on changes later only on the parent table (e.g. inventTable) rather than checking the changes in all tables related to the inventTable. This will be done by creating triggers in the child tables to touch the parent tables. AX made this a simple process, you just need to define a query that includes all the tables needed then simple pass this to the createTouchTriggersForQuery method:
server static void aaChangeTracking_2_CreateTriggers(Args _args)
{
    Query                   query;
    AifChangeTrackingTable  ctTable;
    ;
 
    //Need to be run on server side
    new AifChangeTrackingPermission().assert();
 
    query = new Query("AxdItem");
 
    AifChangeTrackingConfiguration::createTouchTriggersForQuery("TestScope1", AifChangeTrackingTriggerType::AfterInsert, query);
    AifChangeTrackingConfiguration::createTouchTriggersForQuery("TestScope1", AifChangeTrackingTriggerType::AfterUpdate, query);
 
    // Revert the permission assert
    CodeAccessPermission::revertAssert();
}

Job 3:

Tracking the versions will be saved in a table in AX called AifSQLCTVersion. The following code will update this version control table:
static void aaChangeTracking_3_UpdateTrackingVersion(Args _args)
{
    container   conVersion;
    ;
 
    //To record the newest version
    //There is a standard AX batch job that does this
    AifSqlCtChangeTracking::recordCurrentVersion();
}
This can be run as a batch job by calling the following batch job: AifChangeTrackingVersionUpdateJob
Job 4:
To find the changes, I created a batch job that will define a query for the inventTable only. Remember in job 2 we created triggers for the child tables to touch the inventTable. So, in this case we don’t need to check the changes in the child tables (e.g. invnetTableModule). we only need to call the inventTable.
In the following example, the user needsto pass the “getChanges” method two parameters:
1- AifChangeTrackingTable temp table (called it “ctTable” – the system will populate this table with the changes and the versions for the changes. it will also populate the recids for the records changed)
2- utcDateTime (This will be the change needed to be tracked since which dateTime)
server static void aaChangeTracking_4_GetChangedTable(Args _args)
{
    Query                   _query;
    QueryBuildDataSource    _qbds;
    AifChangeTracking       _changeTracking;
    utcDateTime             _dateTimeYesterday;
    AifChangeTrackingTable  ctTable;
    ;
 
    //Change the date based on what is needed
    _dateTimeYesterday = DateTimeUtil::addDays(DateTimeUtil::getSystemDateTime(), -2);
 
    new AifChangeTrackingPermission().assert();
 
    _query = new Query();
    _qbds = _query.addDataSource(tableNum(InventTable));
 
    _changeTracking = AifChangeTracking::construct(_query);
 
    _changeTracking.getChanges(_dateTimeYesterday, ctTable);
 
    while select ctTable
    {
        info(strFmt("%1", ctTable.KeyField_RecId));
    }
 
    CodeAccessPermission::revertAssert();
}
And that’s it! The query returns the list of items that have been updated since the specified date and you can use the results of the query in whatever way you need.