February Blog Series: Tools We Love
Insights from: Bill Concepcion (Fullscope Technical Consultant) and Kevin Brock (Fullscope Systems Architect)
Table relations define how one table relates to another table. It is quite common to have a method from one table to get the associated record of the related table. For example, in the table SalesLine, there is a relation defined between table SalesLine and the parent table SalesTable. Further, there is a method in SalesLine to get the parent SalesTable record of the current SalesLine record. Such methods are called navigational methods because it allows the developer via code to navigate from one record to another related record. Additionally, these methods tend to follow the same rules as defined in the table relation.
This is the traditional way of creating navigational methods. However, starting with Microsoft Dynamics AX 2012, if the user creates a foreign key relation on a table and looks at the properties for that relation, the user will notice some new properties as shown below.
There's one in particular to look at for this discussion: CreateNavigationPropertyMethods. Looking at the name of this property, the word "Navigation" certainly sticks out. Could it be that this is a new way of creating navigation methods, automatically? This would certainly be useful.
The way the CreateNavigationProperyMethods property works is, if set to "Yes," a method with the same name as the relation is created. If the property shown above was set to Yes, then, in X++, when you type "salesLine" the user will see the automatically created navigation method called AgreementLine available in the list of methods as shown below:
This AgreementLine method is created by the kernel and is not listed in the methods list for SalesLine in the AOT. Looking at the help text from the tool tip for the method AgreementLine, it looks up the related record to the SalesLine record in the AgreementLine table. In fact, if the user were to test this out in code, it works like a traditionally created navigation method. Consider the job below that uses both the automatically created navigation method and a traditional navigation method for comparison purposes. The traditional navigation method inventDim, which is defined in standard Microsoft Dynamics AX, is compared against the automatically generated navigation method inventDimTest by setting "Yes" to the CreateNavigationPropertyMethods property. So as not to conflict with the traditionally created navigation method, inventDim, also set the method name by setting the NavigationPropertyMethodNameOverride property to inventDimTest on the relation InventDim from the table SalesLine.
The user is simply modifying temporarily the InventSiteId of the associated SalesLine record to 'zz' and reread the InventDim record and reexamine the InventSiteId. When executed, what is shown in the info log is seen below.
How cool is that! The user doesn't have to manually create these navigational methods. Further, these automatically created navigation methods look up the related records according to the rules in the relation. Manually created navigation methods are not guaranteed to abide or continue to abide by the relation definition. Two thumbs up!! There is consistency!
Looking back on the tooltip for the kernel generated navigation method AgreementLine above, it is strange that the parameter for the method AgreementLine is not a boolean but an AgreementLine buffer. Traditionally, manually created navigation methods have a boolean parameter to determine whether to select the related record for update. This kernel generated navigation method doesn't seem to allow that option. Now, if the user wants to navigate to the related record with the intent of modifying it, the user would either add an extra statement to call the selectForUpdate method or just abandon the kernel generated method altogether and stick with the traditional process of manually creating the navigation method. A user might think that Microsoft should easily see the problem with this and wonder why they did it that way in the first place. Well, the problem isn't with Microsoft. They designed it that way deliberately. The problem is with the user's assumption. The user is assuming this is the way these kernel generated navigation methods are to be used. When a senior developer in Microsoft noticed this behavior with a kernel generated navigation method, he asked one of the architects in Microsoft if this is how it is meant to be used. The architect answered absolutely not! It is meant to be used with the UnitOfWork class.
'What is the problem?' one might ask. It works just like the traditional navigation methods, albeit, without the _forUpdate boolean parameter. Most of the time that it is used, it works just like the traditional navigation methods. However, there is one scenario that was found where it doesn't and that's when it's utilized along with form data sources. Reconsidering the job above, make it part of a class rather than a job.
Class NavigationalDemo is created with one method runTest as defined above. This version of runTest now takes a SalesLine buffer. This buffer is going to be a SalesLine buffer from a form data source. Implementing the logistics of passing a SalesLine form data source buffer will be up to the reader. Now observe the output when a SalesLine form data source buffer is passed in.
This time the kernel generated navigational method does not re-read the database! It appears it's reading from some cache. Now the warning of the Microsoft architect comes back to mind. This is not what it is designed for, but, rather designed to work with the UnitOfWork class. Further, all Microsoft documentation regarding the kernel generation navigation methods are all in the context of UnitOfWork.
It appears to work "correctly" based on initial assumptions about automatically created navigation methods otherwise. But, remember, the assumption is incorrect. This shows that if the user decides to use this outside the context of UnitOfWork, it is to be used at his peril. It's possible that Microsoft may further evolve these type of methods in a way a user would not expect from traditional navigation methods. It should be considered best practice to continue to create and use traditional navigation methods outside the context of UnitOfWork.