MVP Summit 2024

Microsoft MVP Summit is an annual conference in Redmond (USA), where Microsoft Most Valuable Professionals (MVPs) meet to share experience, learn about new features coming to Microsoft products and to give feedback to Microsoft on what works well and where attention is needed.

I just came back from this year’s one and want to share some recollections.

Discussions there are covered by a Non-Disclosure Agreement (NDA), therefore I can’t share what we talked about. But I won’t reveal anything surprising by saying that a lot of investment is going to AI (such as copilots).

There wasn’t much content on F&O as such, but I attended many sessions about Power Platform (Power Apps, Dataverse…). Because there were tracks about many technologies in parallel, I’m now catching up with recordings of what I couldn’t attend in person, either stuff that I work with (C#, GitHub) or new things to learn (AI).

I was coming to Redmond every year since 2013, but there were no in-person Summits in 2020-2022 and I skipped the one last year, therefore it was my first visit in five years. Several new MVPs were awarded in past years and I finally got a chance to meet some of them. I know some from events in Europe, but MVP Summit is often the only chance to meet MVPs from places like India or Australia.

And it’s not just about new people, but maybe even more about meeting old friends, both MVPs and from Microsoft.

Some pictures with fellow F&O MVPs:

F&O MVPs with Microsoft logo

The Microsoft campus and cities around have changed too. There are new Microsoft buildings (and the F&O team moved there) and a railway from Redmond to Bellevue will open in a few weeks. Next year, it’s going to continue to downtown Seattle, therefore one will be able to get all the way from SeaTac airport to the Microsoft campus by train.

And by the way, I often use the opportunity to visit some places in or around Seattle. This time, I went to Portland (Oregon), mainly to see the local Japanese garden.

Japanese garden - a pond and a waterfall

OfflineAuthenticationAdminEmail overwritten

My F&O environment asked me to log in every time to when I started debugging. I changed OfflineAuthenticationAdminEmail in DynamicsDevConfig.xml (at %USERPROFILE%\Documents\Visual Studio Dynamics 365), but I noticed that the value got overwritten every time when I started Visual Studio and opened an F&O project.

I tried changing the value in j:\AosService\PackagesLocalDirectory\bin\DynamicsDevConfig.xml, but it didn’t have any effect.

After some searching, I found that the value is copied from Provisioning.AdminPrincipalName in web.config (e.g. j:\AosService\WebRoot\web.config). Setting my account name there finally fixed the problem.

Entity collection as OData action parameter

Data entities in F&O can be exposed through OData protocol. You can also add OData actions by creating methods in X++ and decorating them with SysODataCollectionAttribute.

You can see an example in MssLeaveRequestDateEntity.getDirects():

[
    SysODataActionAttribute('getDirects', false),
    SysODataCollectionAttribute('return', Types::Record, 'MssLeaveRequestDateEntity')
]
public static List getDirects(
    str reportingPersonnelNumber,
    date fromDate,
    date toDate,
    LeaveRequestApprovalStatus leaveRequestStatus)
{
    List list = new List(Types::Record);
    ...
    return list;
}

It has parameters of several types and it returns a collection of entity records (additional metadata is provided in SysODataActionAttribute).

Now what if you want to add such a collection as an input parameter? It sounds straightforward, but if you do it, the action will completely disappear from the entity because it fails to generate.

I was able to get a confirmation that such a parameter type isn’t supported. It’s another case when event logs helped me. Namely, I found a warning in Application and Services Logs > Microsoft > Dynamics > AX-ODataService > Operational log. At the General tab, it said merely OData Metadata Build Warning, but the Details tab revealed much more:

ComponentNameODataServiceMetadataBuilder
infoMessageAction myTest (Method = myTest) on Entity MyEntity has a parameter (Type = Microsoft.Dynamics.Ax.Xpp.List, Name = inRecords) that resolves to an EntityType collection, which is not currently supported. Remove the parameter.

All right, so it’s explicitly unsupported. Note that this is just about a collection of records; you can use collections of primitive types such as numbers and strings.

The fact that this kind of warning can be found in event logs may be the most important information in this blog post. It may help with debugging of other issues related to OData in F&O.

Happy New Year 2024

Happy New Year! (To everyone who follows Gregorian calendar :)).

Let me look a bit at what I did in 2023 and what I expect in 2024.

I keep working for a large end-user company. I do a lot of different things there (X++, Azure Functions, code reviews, Powershell, DevOps processes and so on), but a lot of that is focused on the single company and it’s not to be shared with the community.

A new Dynamics community site was launched. It’s developed in a different way, which should make its development more agile. On the other hand, there are still quite a few bugs and missing features and their resolution has been slow so far. But progress is being made, so hopefully we’ll have a much better site at the end of 2024.

This year, unified development experience for F&O will become generally available. I still haven’t got chance to play with it too much, which is a pity. I’m keen to, hopefully it’ll change soon. Also, changes in storage pricing are expected in near future, which is an important topic for the new development/admin experience.

We can expect more and more convergence and integration of F&O and Power Platform. I made just a few Power Apps and flows in 2023, let’s see if there will be more in 2024.

In March, I’m going to attend Microsoft MVP Summit in Redmond. It’s a conference for MVPs and Microsoft employees, where MVPs have a chance to meet each other and Microsoft product teams, learn about upcoming features, provide feedback and so on. The social part is important; there are people that I’d have never met in person if I didn’t attend MVP Summits. There were no summits in the era of Covid and I skipped it last year, therefore it’ll be my first summit since 2019.

In May, I’m going to DynamicsMinds conference in Slovenia. I gave a few talks there last year and it was a great event, so I’m happy to come again. I hope to see some familiar faces there!

Query/QueryRun with temporary tables (AX/F&O)

I noticed that some developers believe that Query* classes can’t be used to query temporary tables. It is possible; it just requires an extra step that isn’t needed with regular tables.

When working with temporary tables, each buffer (variable) of the same table can refer to a different set of temporary data. Therefore using the right buffer (reference to a particular data set) is crucial. This is true for select statements in code, form data sources, and for Query* classes as well.

If you want to query temporary tables with Query* classes, you’ll define a query (with classes like Query and QueryBuildDataSource) in exactly the same way as with regular tables. The place where you must pass references to temporary data sets is an instance of QueryRun class, namely setCursor() (or setRecord()) method.

If the query uses several temporary tables, simply call setCursor() several times – the system will find the data source for the given table. The method also has an extra parameter (_occurrence) for the case when you have multiple data sources for the same table.

Here is a complete example using standard tables, therefore anyone can simply copy and run it. It shows all steps – inserting data to temporary tables, creating a query, passing temporary tables to a QueryRun object, running the query and showing returned records. It uses F&O syntax, but the overall approach is the same in Dynamics AX too.

TmpTableName name;
 
name.RefTableId = 1;
name.TableName = 'TableA';
name.insert();
 
TmpTableIdMap map;
map.MainTableId = 1;
map.MainFieldId = 42;
map.insert();
 
Query query = new Query();
QueryBuildDataSource nameDs = query.addDataSource(tableNum(TmpTableName));
 
QueryBuildDataSource mapDs = nameDs.addDataSource(tableNum(TmpTableIdMap));
mapDs.addLink(fieldNum(TmpTableName, RefTableId), fieldNum(TmpTableIdMap, MainTableId));
 
QueryRun qr = new QueryRun(query);
qr.setCursor(name);
qr.setCursor(map);
 
while (qr.next())
{
    TmpTableName nameFetched = qr.get(tableNum(TmpTableName));
    TmpTableIdMap mapFetched = qr.get(tableNum(TmpTableIdMap));
 
    info(strFmt('%1 - %2', nameFetched.TableName, mapFetched.MainFieldId));
}