Thursday, April 22, 2021

Maximum Size for Business Events

Business Events help to integrate D365FO with other systems; they are supposed to be specific and small. But how exactly small should they be? What is the maximum size for one message?

If we take a look at the code, we will see that the maximum is driven by the type of Business Event End Point:
























Some of these classes specifically set their max. 



For example, BusinessEventsServiceBusAdapter even implements some logic here.




As per Flow and HTTP, it is defined as 1MB minus 4KB for the header.



So, we have two conclusions here:

- we have to check these hard-coded limits directly in the code, as they may be changed in future;

- if you need to send a chunk of information bigger than the maximum size, you'd better revise your solution. For example, instead of sending a file you can send just its Azure Storage locator;

Another option can be developing your own End Point.

Some useful links.

How to develop Business Events

How to use Business Events

Example of using Microsoft Power Automate (Flow) to integrate with D365FO via Business Events  

Monday, April 12, 2021

Switch in View Computed column

 If you need to compare multiple fields in your view while populating a computed column, take into consideration that the standard implementation of method SysComputedColumn::switch uses a map enumerator. 

public static client server str switch(str _controlExpression, Map _comparisonExpressionMap, str _defaultExpression)
    {
        MapEnumerator  mapEnumerator;
        str caseExpression = '';

        caseExpression ='CASE ' + _controlExpression;
        mapEnumerator = _comparisonExpressionMap.getEnumerator();
        while (mapEnumerator.moveNext())
        {
            caseExpression += ' WHEN ' + mapEnumerator.currentKey() + ' THEN ' + mapEnumerator.currentValue();
        }
        caseExpression += ' ELSE ' + _defaultExpression;
        caseExpression += ' END';

        return caseExpression;
    }

It means that your given order will be replaced in final SQL clause by alphabetical one of your keys.

CASE  
	WHEN (T4.PROJID1) != ('') THEN T4.PROJNAME1 
	WHEN (T4.PROJID2) != ('') THEN T4.PROJNAME2 
	WHEN (T4.PROJID3) != ('') THEN T4.PROJNAME3 
	ELSE T4.PROJNAME END

For example, we want to populate field mgcProjInvoiceGLTransView.ParentProjectName for a given project id up to three level up in the project hierarchy. Let's assume that they are prepopulated in mgcProjInvoiceOnAccView view: projId1 is the parent of projId, projId2 is the parent of projId1, and so on.

Once we reference this computed column to the following method, we will get a new order in CASE construction on the SQL side.

 private static str projNameParent()
    {
        str         ret;
        SysComputedColumnBase::switch cannot keep a given order while using Map enumerator; so we put the SQL string as a string constant
        tableName   viewName                    = identifierStr(mgcProjInvoiceGLTransView);
        tableName   tableName                   = identifierStr(mgcProjInvoiceOnAccView);
        str         compareValue                = '';


        Map         comparisonExpressionMap     = SysComputedColumn::comparisionExpressionMap();
        str         fieldNameProjName3          = SysComputedColumn::returnField(viewName, tableName, fieldStr(mgcProjInvoiceOnAccView, ProjName3));
        str         fieldNameProjName2          = SysComputedColumn::returnField(viewName, tableName, fieldStr(mgcProjInvoiceOnAccView, ProjName2));
        str         fieldNameProjName1          = SysComputedColumn::returnField(viewName, tableName, fieldStr(mgcProjInvoiceOnAccView, ProjName1));
        str         fieldNameProjName           = SysComputedColumn::returnField(viewName, tableName, fieldStr(mgcProjInvoiceOnAccView, ProjName));

        comparisonExpressionMap.insert(
        SysComputedColumn::notEqualExpression(
            SysComputedColumn::comparisonField(viewName, tableName, fieldStr(mgcProjInvoiceOnAccView, ProjId3)),
            SysComputedColumn::comparisonLiteral(compareValue)),
        fieldNameProjName3);

        comparisonExpressionMap.insert(
        SysComputedColumn::notEqualExpression(
            SysComputedColumn::comparisonField(viewName, tableName, fieldStr(mgcProjInvoiceOnAccView, ProjId2)),
            SysComputedColumn::comparisonLiteral(compareValue)),
        fieldNameProjName2);

        comparisonExpressionMap.insert(
        SysComputedColumn::notEqualExpression(
            SysComputedColumn::comparisonField(viewName, tableName, fieldStr(mgcProjInvoiceOnAccView, ProjId1)),
            SysComputedColumn::comparisonLiteral(compareValue)),
        fieldNameProjName1);

        ret       = SysComputedColumn::switch(
                                                '',
                                                comparisonExpressionMap,
                                                fieldNameProjName);
        return ret;
    }

Of course, it always returns  the name of the parent on the first level, even though it had its own parent, which is incorrect result. 

Therefore, the easiest way is to replace the aforementioned construction with a simple string.

 private static str projNameParent()
    {
        str         ret;
        ret = "CASE WHEN (T4.PROJID3) != ('') THEN T4.PROJNAME3 WHEN (T4.PROJID2) != ('') THEN T4.PROJNAME2 WHEN (T4.PROJID1) != ('') THEN T4.PROJNAME1 ELSE T4.PROJNAME END";
        return ret;
    }