Code highlighting

Showing posts with label executeQuery. Show all posts
Showing posts with label executeQuery. Show all posts

Monday, May 11, 2015

Tutorial: Caching display methods

Overview

Caching is an important part of the Microsoft Dynamics AX application. It allows us to significantly speed up certain processes by avoiding redundant round trips to the server and the database. Display/Edit methods are a good example of where caching is helpful, as it significantly increases the overall responsiveness of a form.
In AX 2012 R3 there are a number of ways you can specify that a display method needs to be cached. Below is a list, as well as a brief description and an xpo with a tutorial form to show the various options.
  • Invoke cacheAddMethod(MethodName) in the init() method of the data source
This is the only option that was available in older versions of Dynamics AX.
All cached data methods are calculated and cached in executeQuery of the data source they belong to. That is why it is important to call the above method in the init() of the data source, once the data source is initialized, but the data hasn't been retrieved yet. The optional second parameter controls whether or not the display method value should be refreshed when data is written to DB
  • Add the SysClientCacheDataMethodAttribute attribute on the display method
Now that attributes are available, caching a display method is as easy as adding an attribute on the method. It has an optional parameter same as the above method. Note, that marking a display method with this attribute will mean that it is cached on all forms the method is used on. So make sure that is what you want to happen before adding it.
  • Set the “Cache data method” property on the display method form control to Yes
This is, again, a new capability in the latest version of AX, that is pretty much identical to calling the cacheAddMethod, just much easier.
  • Set the “Cache data method” property on the display method form control to Auto, where the data source is read-only
Now, this is something that is done automatically by the kernel. If the data source which the display method belongs too is read-only, and Cache data method property on the corresponding control is set to Auto, the kernel will cache this method. This is smart, as the value can’t really change. You can still change the property to No, if there’s a reason why you want the display method not to be cached.

Additional info

On top of the above, I would also like to mention a few other things.

  • There are 2 kernel-defined methods, called titleFields and parentTitleFields, which are always being cached automatically by the kernel. The titleFields method corresponds to the 2 Title Fields that you can set on the table level. Then, if you have this table as your TitleDatasource on the form design, the 2 fields selected will be shown in the caption of the form. parentTitleFields is very similar, only it displays the title fields of the parent data source table, which is relevant in a header-lines (dynalinked datasources) kind of scenario. As a fallback, it shows the titleFields.
  • There are methods for managing the cached methods, one of them being cacheCalculateMethod, which asks the server to re-calculate the method value and update the cache for the current record. Note that it will only take effect if the method has previously been cached. In the tutorial project I am using this method in the form method runHack to refresh the value of 2 of the display methods. 

Example

You can download the xpo project with the tutorial from my OneDrive.
Upon opening the form in the project, some data will be pulled into the form. More specifically, the first 3 items/customers/vendors/sites/warehouses. For each of them a display method is present, and one of the above approaches is used to cache it.

Step 1



None of the display methods are cached. As you can see, nothing extra was done in executeQuery of the main data source, but itemName was cached for all 3 records for the read-only data source, as the control has "Cache data method" property set to Auto. This happens even though the tab page is not the active one, so it wasn't really yet necessary to calculate these values. Maybe the user won't even navigate to that tab page. You should consider this carefully when designing a form, and always aim at increasing the overall responsiveness of a form by only executing the appropriate display methods.

Step 2 and 3



inventSiteName and vendName were cached (through one of the options above). As you can see, now their values are calculated once for each of the records shown. InventLocationName and custName are still not cached.
Clicking on Refresh result button will show any extra calls to the display methods since the form started. As you’ll quickly notice, the non-cached methods are being called every time something hints at covering up the value of the control, or rather every time AX thinks it needs to recalculate the value, which is VERY OFTEN. That should make it pretty clear how useful caching is for display methods.

Step 4



Now inventLocationName was also cached, and the only method that is remaining and not cached is custName. You can see how more and more methods are executed in one batch on the server as part of the executeQuery method on the data source.

Step 5



As the last step, I have added 2 standalone controls that show the Title fields of the main data source table, and started logging the time method caption is invoked. As I have written above, titleFields and parentTitleFields are cached automatically, but you can't really see it, as it is not possible to override it. caption is the closest you can get to it, so I figured it's worth showing that. You can see the method is evaluated right after all data source queries have executed (as it relies on the currently selected record). It is evaluated first time based on the selected data source record, and then again when focus actually goes to the grid, so that the first record is explicitly selected. It will be invoked again whenever the active record changes. Note that unlike custName method, it does not get constantly refreshed.

Play around with the form and let me know if you have any questions or comments.
Thanks

Friday, March 26, 2010

Tutorial: refresh, reread, research, executeQuery - which one to use?

X++ developers seem to be having a lot of trouble with these 4 datasource methods, no matter how senior they are in AX.
So I decided to make a small hands-on tutorial, demonstrating the common usage scenario for each of the methods. I have ordered the methods based on the impact on the rows being displayed in the grid.
You can download the xpo with the tutorial on my SkyDrive.

1. Common mistakes

Often, developers call 2 of the mentioned methods in the following order:
formDataSource.refresh()
formDataSource.research()

or
formDataSource.reread()
formDataSource.research()

or
formDataSource.research()
formDataSource.executeQuery()

or
formDataSource.research()
formDataSource.refresh() / formDataSource.reread()

All of these are wrong, or at least partially redundant.
Hopefully, after reading the full post, there will be no questions as to why they are wrong. Leave a comment to this post if one of them is still unclear, and I will try to explain in more detail.

2. Refresh

This method basically refreshes the data displayed in the form controls with whatever is stored in the form cache for that particular datasource record. Calling refresh() method will NOT reread the record from the database. So if changes happened to the record in another process, these will not be shown after executing refresh().
refreshEx
Does a redraw of the grid rows, depending on the optional argment for specifying the number of the record to refresh (and this means the actual row number in the grid, which is less useful for AX devs). Special argument values include -1, which means that all records will be redrawn, and -2, which redraws all marked records and records with displayOptions. Default argument value is -2.
This method should be used sparingly, in cases where multiple rows from the grid are updated, resulting in changes in their displayOptions, as an example. So you should avoid using it as a replacement for refresh(), since they actually have completely different implementations in the kernel.
Also, note, that refreshEx() only redraws the grid, so the controls not in the grid might still contain outdated values. Refresh() updates everything, since this is its intention.

3. Reread

Calling reread() will query the database and re-read the current record contents into the datasource form cache. This will not display the changes on the form until a redraw of the grid contents happens (for example, when you navigate away from the row or re-open the form).
You should not use it to refresh the form data if you have through code added or removed records. For this, you would use a different method described below.
How are these 2 methods commonly used?
Usually, when you change some values in the current record through some code (for example, when the user clicks on a button), and update the database by calling update method on the table buffer, you would want to show the user the changes that happened.
In this case, you would call reread() method to update the datasource form cache with the values from the database (this will not update the screen), and then call refresh() to actually redraw the grid and show the changes to the user.
Clicking buttons with SaveRecord == Yes
Each button has a property SaveRecord, which is by default set to Yes. Whenever you click a button, the changes you have done in the current record are saved to the database. So calling reread will not restore the original record values, as some expect. If that is the user expectation, you as a developer should set the property to No.

4. Research

Calling research() will rerun the existing form query against the database, therefore updating the list with new/removed records as well as updating all existing rows. This will honor any existing filters and sorting on the form, that were set by the user.
Research(true)
The research method starting with AX 2009 accepts an optional boolean argument _retainPosition. If you call research(true), the cursor position in the grid will be preserved after the data has been refreshed. This is an extremely useful addition, which solves most of the problems with cursor positioning (findRecord method is the alternative, but this method is very slow).

5. ExecuteQuery

Calling executeQuery() will also rerun the query and update/add/delete the rows in the grid. The difference in behavior from research is described below.
ExecuteQuery should be used if you have modified the query in your code and need to refresh the form to display the data based on the updated query.
formDataSource.queryRun().query() vs formDataSource.query()
An important thing to mention here is that the form has 2 instances of the query object - one is the original datasource query (stored in formDataSource.query()), and the other is the currently used query with any user filters applied (stored in formDataSource.queryRun().query()).
When the research method is called, a new instance of the queryRun is created, using the formDataSource.queryRun().query() as the basis. Therefore, if the user has set up some filters on the displayed data, those will be preserved.
This is useful, for example, when multiple users work with a certain form, each user has his own filters set up for displaying only relevant data, and rows get inserted into the underlying table externally (for example, through AIF).
Calling executeQuery, on the other hand, will use the original query as the basis, therefore removing any user filters.
This is a distinction that everyone should understand when using research/executeQuery methods in order to prevent possible collisions with the user filters when updating the query.