Table extension framework

Assume that we have table TableExtensionParent and we want to make extension table TableExtensionChild

1. At TableExtensionChild table Add new relation named TableExtensionParent, table TableExtensionParent. Add new field ForeigKey -> PrimaryKey based at newly created TableExtensionChild table

Reference field to the primary table will be added automatically and it should look like:


2. Create a unique index for the reference field that points back to TableExtensionParent .

3. Create methods at child table
insert
public void insert()
{
    if (0 != this.TableExtensionParent)
    {
        // only insert if the FK is valid
        super();
    }
}
update
public void update()
{
    if (0 != this.TableExtensionParent)
    {
        // only update if the FK is valid
        super();
    }
}
find
public static TableExtensionChild find(
    recId               _recId,
    boolean             _forUpdate          = false,
    ConcurrencyModel    _concurrencyModel   = ConcurrencyModel::Auto)
{
    TableExtensionChild tableExtensionChild;

    tableExtensionChild.selectForUpdate(_forUpdate);
    if (_forUpdate && _concurrencyModel != ConcurrencyModel::Auto)
    {
        tableExtensionChild.concurrencyModel(_concurrencyModel);
    }

    select firstonly tableExtensionChild where tableExtensionChild.RecId == _recId;

    return tableExtensionChild;
}
findByParent
public static TableExtensionChild findByTableExtensionParent(RecId _tableExtensionParentRecId, boolean _forUpdate = false)
{
    TableExtensionChild tableExtensionChild;

    tableExtensionChild.selectForUpdate(_forUpdate);

    if (_tableExtensionParentRecId != 0)
    {
        select firstonly * from tableExtensionChild
            where tableExtensionChild.TableExtensionParent == _tableExtensionParentRecId;
    }

    return tableExtensionChild;
}

4. Update parent table and create PackedExtensions field

5. Add a delete action from the parent table to the child table.

6. Create/Update methods at parent table

Method to get child table:
public TableExtensionChild tableExtensionChild()
{
    return this.SysExtensionSerializerMap::getExTensiontable(tableNum(TableExtensionChild));
}

Method to pack the child table on the parent table packed extensions:
public void packTableExtensionChild(TableExtensionChild _tableExtensionChild)
{
    _tableExtensionChild.TableExtensionParent = this.RecId;
    this.SysExtensionSerializerMap::packExtensionTable(_tableExtensionChild);
}

Method to add a child table data source, that you might use in future to modify Query
public static QueryBuildDataSource addDataSource(QueryBuildDataSource _parentDataSource, TableId _tableId, JoinMode _joinMode = JoinMode::InnerJoin)
{
    QueryBuildDataSource qbds;

    if (null == _parentDataSource || tableNum(TableExtensionParent) != _parentDataSource.table() || tableNum(TableExtensionChild) != _tableId)
    {
        throw error(error::wrongUseOfFunction(funcName()));
    }

    switch (_tableId)
    {
        case tableNum(TableExtensionChild):
            qbds = _parentDataSource.addDataSource(_tableId);
            qbds.addLink(fieldNum(TableExtensionParent, RecId), fieldNum(TableExtensionChild, TableExtensionParent));

            qbds.fetchMode(QueryFetchMode::One2One);
            qbds.joinMode(_joinMode);
            break;
    }

    return qbds;
}

Create/Update insert method:
public void insert()
{
    super();

    this.SysExtensionSerializerMap::postInsert();
}

Create/Update update method:
public void update()
{
    super();

    this.SysExtensionSerializerMap::postUpdate();
}

7. Integrate with the table extensions framework
Add parent table to SysExtensionSerializerMap map and child table to SysExtensionSerializerExtensionMap

8. Create simple form to test result
Please be aware that write method of parent datasource should be modified:
public void write()
{
    super();
    TableExtensionChild.SysExtensionSerializerExtensionMap::insertAfterBaseTable(TableExtensionParent);
}
write method of child table triggers first and as a result child record will not be saved as it's insert and update methods does not allow it without correct foreign key.

Download test table extension shared project

If you know russian language you can also watch more detailed video about extension framework:
https://www.youtube.com/watch?v=YrfHtAKRMbk


Support The Author

 If you found value in what I share, I've set up a Buy Me a Coffee page as a way to show your support.

Buy Me a Coffee

Post a Comment


All Comments


Sasha Nazarov 04 June 2016

Hello

Thanks for the great article.

I would like to add that if the parent table's primary key is not RecId-based, but the table has CreateRecIdIndex property set to Yes, then the relation on step #1 should be created based on "Single field AlternateKey based"; otherwise the new feld will potentially have wrong type, and it will not be possible to use it in the SysExtensionSerializerExtensionMap map.

Also, people have to remember to add a DeleteAction to the parent table.


Santosh 21 July 2016

this approach does not work if you are exposing these 2 parent and child tables through an AIF document service.

1. Child table record gets created as part of parent table record

2. Then AIF document service framework tries to create the Child table record again and throws an error something on the lines "Record already exists".


Search

About

DaxOnline.org is free platform that allows you to quickly store and reuse snippets, notes, articles related to Dynamics 365 FO.

Authors are allowed to set their own "buy me a coffee" link.
Join us.

Blog Tags