Search code examples
axaptadynamics-ax-2012

Dynamics AX 2012 - Grid Control linkPhysicalTableInstance to TempTable, first record inserted doesn't appear in Grid, fine afterwards


The problem related to using table buffers in AX 2012 with Grid Controls, where first time additions to the form's tempDB were not displayed in real time (but were persistent and subsequent additions worked fine thereafter).

I resolved the problem with help from DAX legend Martin Dráb and Brandon Weise on the Dynamics Community MSDN but I'm posting on SO in case it helps others (as I couldn't find anything close), and I don’t think it hurts the community to add more Dynamics AX content on SO. There are also some learnings to be had about how physical tables link to tempDBs and their relationship to the form's datasource.

Original thread: https://community.dynamics.com/ax/f/33/t/225120

Problem:

  • I have a Wizard that generates a new form at runtime, containing a Grid Control.
  • The Wizard passes a reference to one of its temp Tables to the form, in which I use linkPhysicalTableInstance in the runtime form's datasource init() method.
  • The GridControl has an add new record button, which inserts records in to the tmp table reference.
  • The new record is saved to the reference temp table correctly, and displays in the grid when the runtime form is closed and reopened but does not display in the grid immediately after the insert.
  • To add to the weirdness, after a runtime form has been created, a record inserted and then closed, subsequent run time forms do display new record insertions immediately, without needing to be re-opened. Some code snippets below.

Why does this behavior only happen for the first time that data is inserted in to the temp table, but displays fine for subsequent runs of the runtime form?

Creating the runtime form:

        args                    = new Args(formstr(RunTimeFormName));
        formRun             = classFactory.formRunClass(args);
        formRun             .parmRuntimeFormsGridTmpDS(sysWizard.ReferenceToWizardsTableTmp()); // Passing a reference for Wizards tmpTable to form
        formRun             .init();
        formRun             .run();
        formRun             .wait();
        formRun             .detach();

RunTime Form's parmDataSourceMethod:

public void parmRuntimeFormsGridTmpDS(CommentsGridTmp _ReferenceToWizardsTableTmp)
{
    ReferenceToWizardsTableTmp = _ReferenceToWizardsTableTmp;
}
DataSource init() method:
public void init()
{
    super();
    RuntimeFormsGridTmpDS.linkPhysicalTableInstance(ReferenceToWizardsTableTmp);
}

RunTime Form's New button clicked method:

void clicked()
{
    int64                                             numRows;
    ;
    // Refresh records loaded in grid DS to ensure correct number of records for setting initial index number
// Note:  SomeId is passed in Args() record, its passing fine as is the number of rows count - and replacing with a static value has no effect.

    select count(RecId) from ReferenceToWizardsTableTmp
        where ReferenceToWizardsTableTmp.SomeId == someId;
    numRows                                           = ReferenceToWizardsTableTmp.RecId;
    ReferenceToWizardsTableTmp.Comment                  = "Comment " + int642str(numRows + 1);
    ReferenceToWizardsTableTmp.Filename                 = "";
    ReferenceToWizardsTableTmp.someId                    = someId;
   ReferenceToWizardsTableTmp.insert();

    element                                           .Task(#TaskF5);
//    super();
}

So as described above, the first time that the runtime form is created and a record is inserted, it doesn't display. Reopening the form will display the inserted data fine. Also, after reopening the form any new records inserted appear in the grid in real time. I originally supposed it had to be something to do with the linkToPhysicalTable and where the grid fields look for records to display…

By the way, if you have a better answer or explanation then please feel free to contribute.


Solution

  • Solution

    I have a working solution whereby I run a select statement on the buffer reference prior to the linkPhysicalTableInstance operation (a delete_from tmptable statement has the same effect and is cheaper), which acts to initialize the buffer reference despite it being empty.

    The linkPhysicalTableInstance operation then succeeds at the first run because the buffer properly exists - and changes written to the form DS are now persistent and reflected in the calling Wizard's buffer reference.

    In Addition (from Brandon Weise):

    In case you happen to be jumping tiers in your code, here's a small gotcha to watch out for. https://community.dynamics.com/ax/b/dynamicsaxexperience/archive/2016/01/24/2012-unexpected-degeneration-of-insert-recordset-into-tempdb-buffer

    Techniques That I Found Useful for Investigation

    (Credit to Brandon Weise and Martin Dráb for these)

    1. It does seem that using .linkPhysicalTableInstance(..) to change the underlying temp table associated with a form datasource after it has already initialized produces some weird behavior. This seems to be true even when you can demonstrate with .getPhysicalTableName() that they are linked properly.

      One technique to help is to create your form, and call .init(), but not yet .run(). Then use .linkPhysicalTableInstance() to link the freshly created temp table underlying the data source to your external temp table buffer. In other words, instead of trying to transplant your already created temp table into the form's data source, let the form create the temp table, then let the caller transplant that temp table into its own buffer using .linkPhysicalTableInstance(). Then insert records, then call .run(). If necessary, call .executeQuery() on the form data source after .run().

      I took a scattergun approach to printing the table names throughout initialization and operation, and while the two tables do eventually link correctly with the same table name, they take a roundabout way of getting there.

    2. Inspect the contents of a temp table while debugging, from SQL Server Management Studio using:

      set transaction isolation level read uncommitted;

      select * from tempdb..t107946_BE044A13A9C24283897CA1B59607CBD2;

      Which is easy if you have the table name from .getPhysicalTableName(), but even if you don't know it precisely, it's often easy to locate with a little trial and error. select * from tempdb.sys.tables; The table will of course start with "t" and the table number. Often it's the most recently created one, so sorting by create_date desc floats it to the top, but of course there can be a pool of them.

    3. Review Methods on a Form Data Source which methods you can use when working with records through a datasource.