30 October 2020

D365FO: duplicate model ID's

Each D365FO binary model or model with source code has a model ID. In theory it should be unique, however in real life sometimes model ID conflict could happen. 

Model ID could be found in \Bin\ <model_name>_ModelInfo.md in binary format or \Descriptor\<model_name>.xml file in plain text.

Typically, when you have a model ID conflict on developer VM, the error message will contain conflicting model names, so it is easy to find and fix one of the model ID's. However, on the build VM the error message just says "An item with the same key has already been added", so it is up to you to figure out what is going on.

It could be time consuming to check all the model IDs manually, so I made a simple PowerShell script to retrieve all the model IDs and report any conflicts found.


$rootFolder = "K:\AosService\PackagesLocalDirectory"
$descriptorFolder = "Descriptor"
$binaryFolder = "Bin"

$hashTable = @{}

$conflictCounter = 0

foreach($file in (Get-ChildItem -Path $rootFolder | where {($_.psiscontainer)}))
{
    $descriptorFolderPath = Join-Path -Path $file.FullName -ChildPath $descriptorFolder
    $binaryFolderPath = Join-Path -Path $file.FullName -ChildPath $binaryFolder

    # model with source code    
    if (Test-Path -Path $descriptorFolderPath)
    {
        foreach($xmlFile in (Get-ChildItem -Path $descriptorFolderPath -Recurse -Include *.xml))
        {
            Write-Output $xmlFile.FullName

            $xml = [xml](Get-Content $xmlFile.FullName)
            Write-Output $xml.AxModelInfo.Id

            #check for duplicates
            if ($hashTable[$xml.AxModelInfo.Id])
            {
                $Host.UI.WriteErrorLine("DUPLICATE ID DETECTED!")
                $Host.UI.WriteErrorLine("Previous model with the same id $xml.AxModelInfo.Id :")
                $Host.UI.WriteErrorLine($hashTable[$xml.AxModelInfo.Id])

                $conflictCounter += 1
            }
            else
            {
                $hashTable.Add($xml.AxModelInfo.Id, $xmlFile.FullName)
            }
        }
    }
    
    #binary model, could be both binary and model with source code in the same folder
    if (Test-Path -Path $binaryFolderPath)
    {
        foreach($binaryFile in (Get-ChildItem -Path $binaryFolderPath -Recurse -Include *_ModelInfo.md))
        {
            $zeroSix = [byte]06
            $zeroSixFound = 0
            $bytesFound = [byte[]]::new(4)
            $iterationCounter = 0

            Write-Output $binaryFile.FullName

            foreach($byte in Get-Content $binaryFile.FullName -encoding byte)
            {
                if ($zeroSixFound)
                {
                    $bytesFound[$iterationCounter] = $byte

                    $iterationCounter = $iterationCounter + 1

                    if ($iterationCounter -eq 4)
                    {
                        break
                    }
                }

                # $byte | Format-Hex

                if ($byte -eq $zeroSix)
                {
                    $zeroSixFound = 1
                }
            }

            $binaryModelId = $bytesFound[3] + ($bytesFound[2] * 16) + ($bytesFound[1] * 16 * 16) + ($bytesFound[0] * 16 * 16 * 16)

            Write-Output $binaryModelId

            #check for duplicates
            if ($hashTable[$binaryModelId])
            {
                $Host.UI.WriteErrorLine("DUPLICATE ID DETECTED!")
                $Host.UI.WriteErrorLine("Previous model with the same id $binaryModelId :")
                $Host.UI.WriteErrorLine($hashTable[$binaryModelId])

                $conflictCounter += 1
            }
            else
            {
                $hashTable.Add($binaryModelId, $binaryFile.FullName)
            }
        }
    }
}

Write-Output ""
Write-Output "Conflicts counter: $conflictCounter"

15 October 2020

D365FO: mystery with handling multi selected grid rows from X++

Recently I had a simple task to handle multiple selected records in a form grid when a user clicks a form button. There is a special MultiSelectionHelper X++ class which is typically used in such scenarios. Here is a sample code:


public void clicked()
{
    super();

    YourTable yourTable;

    MultiSelectionHelper helper = MultiSelectionHelper::construct();
    
    helper.parmDatasource(YourTable_ds);

    yourTable = helper.getFirst();
    
    while (yourTable.RecId != 0)
    {
        info(int642Str(yourTable.RecId));

        yourTable = helper.getNext();
    }
}

In my case, however, the code above always displayed only one line in the Infolog, meaning that only one line was selected in the form grid.

After few hours of investigation the root cause of the problem was found. The form button had "Auto Refresh Data" property set to Yes. Call to the super(); methods was causing the grid to be refreshed, thus clearing up all multi selected lines and leaving only one selected lines.


The solution was to move super() call to the end of the method, allowing MultiSelectionHelper class to handle all selected lines properly.

Another mystery case is solved. Next time will be more careful with element properties and super() method calls.