However, having the password and certificate path information available wasnt exactly a good idea at that time (and probably is still relevant. Why would you want to distribute those two together no?)

So came the the License generator, with the password hard coded and everything else seemingly invisible to the person punching the licenses without having someone with knowledge of the axutil and more importantly

What is this black box ? and why cant I just click somewhere

Moving forward a few years later and opening up the code out, (yes the password has been taken out of the code). Its now stored as well, although not very secure, but enough to keep prying eyes out for a bit. (this means rooms for improvement)

Power BI for desktop is a very powerful tool and can be used to mash up data from different sources. It is definitely one up to the Excel plugins and so far seems to be more lightweight and processes things faster
I will go through my findings with PowerBI and Ax to come up with some examples

Open up the the application and we shall create a report that looks like this

AX Sales over time / Region

So once we open up PowerBI, select “Get Data” and select SQL server database

Select the data source

Enter the Database Server, if possible put the database name as well. Ax Databases being so large seems to slow down power BI.

Select the database server and database

We will now select the Tables/Views from the list. You can use the search bar and select the checkbox. Select the following Tables/Views:

CustInvoiceJour

CustGroup

LogisticsPostalAddressView

After selecting the checkboxes, click the “EDIT” button in the pop up window. This will load the data preview and open the “Query Editor”

Edit Table Columns

Edit the following tables to trim the data being retrieved:

LogisticsPostalAddressView

Select the columns: City,CountryReqionID,RecId. Then right click and select “Remove other columns”

CustGroup

Select the columns: CustGroup,Name,DataAreaId. Then right click and select “Remove other columns”

Add a computed field for identification. Click “Add Column” from the ribbon and then “Add custom column”. Set the new column name to CustGroupId and the formula as =[CUSTGROUP]&[DATAAREAID]
Create CustGroupId field for identification

Create a new custom column “CustGroupId” similar to the one done for CustGroup Table

Create a new custom column “InvoiceYear”. the

Once the above is done, Select “Close and load” button, located in the “home” ribbon bar

Relationships

So now that the tables have been defined, we need to set the relationships between them. Relationships in Power Bi, just like powerview in Excel, can only be linked via one field. Hence, why we added a computed column in the tables CustGroup and CustInvoiceJour

Click on “Manage relationships”

Delete any relationships in there (Currently there is one based off the custgroupId, but we shall delete it to make sure we add it all correctly)

Relate CustInvoiceJour > CustGroup

Add a new relation linking CustInvoiceJour to CustGroup using the CustGroupId on both Tables.

Relation between CustInvoiceJour and Custgroup

Relate CustInvoiceJour > LogisticsPostalAddressView

Add a new Relation and related CustInvoiceJour to LogisticsPostalAddressView using the InvoicepostalAddress to the RecId. See image for details

Relation between CustInvoiceJour and LogisticsPostalAddressView

You should now have 2 relations setup. Close the Manage relations window and we will create our report

Creating the report

Bar Chart

in the field list at the right side of the screen, select the fields Name from the Custgroup table and then the InvoiceAmountMST field from the CustInvoiceJour Table. This will create a Table. Select the Bar chart option on the right side and it will change the view

Map

Click on a blank area in the report. From the field list select InvoiceAmountMST from the CustInvoiceJour Table and then CountryRegionId from the LogisticsPostalAddress. Then select the Map view

Sales by region over time

This will show how sales figures have been flowing by the countries
Click on a blank area in the report canvas. From the field list select InvoiceAmountMST and InvoiceYear from the CustInvoiceJournal. Make sure the InvoiceYear is on the X-Axis. Then select CountryRegionId from LogisticsPostalAddressView. Change the graph type to Line

So you got updated Model(s) or hotfix(es) and need to make sure your ISV or VAR or CUS / USR layer code has been merged.

Lets see what your options are:

Create a project for each model and then compare then to the upper layers for changes.
— You spend a lot of time giving yourself carpal syndrome

Auto code upgrade
— It only manages hotfixes. The code that needs to be manually reviewed can be overwhelming? (someone please correct me on that)

You write some script to find out how to do this

Magic beans
— You don’t get them

Ok, so I wrote some SQL scripts to do this, however it was getting too complex for me and I knew I could do it faster with in memory processing with C#. Although I can argue with myself that SQL can do a better job with this. However, I went to the path of C#

Tables used:

SysElementType – This stores what a node in the AOT is classified as. E.g. TableInstance, ClassInstanceMethod, BaseEnum. This is purely to translate an integer to a human readable value.

SysModelElement – This is where the definition of each object is. So every node in the AOT is one record here. This record also links it to the Parent Element. So a record which is the table field, will have its parent element to the Table itself. A parent element can have another parent element too, e.g. a form element inside a group

SysModelElementData – This is where the code for the SysModelElement is stored. And it it stored for each Model (not layer). Moment I override the code in the USR layer for the SalesFormLetter classdeclaration in Ax, it will create a record in this table with the new code. So use it as a caution, if you ever query this table, be careful because you could be transferring a “lot” of data across the wire

Combining SysModelElementData, i can find what all other models use the same element. Then link it up to sysModelElement and find more information about the AOT object that has changed.

Armed with this knowledge I went and created C# console application

The project currently compares models. So if you want to know what has affected the VAR and USR layer models with the addition of a particular SYP layer model, then you send the list of SYP model id’s and then the list of VAR and USR layer models as a csv string. This does a fast compare and outputs what objects you need to look at.

The shortcomings / bugs of this so far:

Security, menu items dont get compared because of the way this is stored

Currently you need to compare models. I would like to say i need to know what objects are affected by these SYP models and then get a list of objects on all the higher layers

Moral of the story:NEVER KILL A COMPILER WORKER WINDOW EVER

There was a time I “accidentally” closed a compiler widow of a 10 worker process in the 2nd run.
Even though the main window notified that a worker terminated without any reason code, it still continued the process and reported that the compile was successful. Including an error log with a clean compile log (except for warnings, who looks at warnings!!! 😉 )
The next part which is compile the CIL went a bit weird.

The CIL compiler would start and AX stared at me for about a minute (as if its doing something, even the processors reported a good 98% usage for the AOS process) and then the AX screen came back to life.

The weirdness continues

It didn’t report anything.

There was no infolog to tell me that the services were generated, or the CIL compiler had failed.

There was nothing in the event log.

I even did a AOS restart with the deletion of the xppil folder, and yet I got the same result.

There was no CIL compiler log generated either.

So turns out (whatever happens inside the engine) that you should NEVER KILL A COMPILER WORKER WINDOW EVER !

When ever adding a new hot-fix or a new model from another source we need to compare the code touch points between that and existing code.

Ax makes that easy using the compare layers tool, however that only compares layers

So the other way is to go code upgrade and use those tools which may or may not work depending on your case (for example you put a 3rd part model and want to compare what var or cus or usr layer code you need to change)

This brings to writing x++ code or so in yummy SQL

Requirements:

Know what models have changed

Know what models will be affected / need to be compared

Know what models have changed

You know what hotfixes have been put in and there may be more than one. so note the numbers of the hotfixes or models that were added. Use the Get-AxModel command to get the models Ids for them

Know what models will be affected / need to be compared

You will probably need to know all the Models Id’s from the higher layers. So if you have a hotfix that you added then take a note of all model id’s that are a part of isv, isp, var, vap, cus, cup, usr, usp …. you know what i mean

Hopefully this gives an easier way to figure out what has changed and reduces your time. If this post does help you let me know, I am planning to invest in a query to further give more details about each element.

From the follow up from Part 1, this post is more about how to find what models are dependent on what. I have created a console app which outputs csv files and can be downloaded here.

The inner workings:

So we know that ModelElement and ModelElementData hold the AOT objects for the respective models.

ModelElementData is the granular data. So a table field, a class method, a form control are stored here. They link to their parent using the ParentHandle field, or straight to the Main AOT object using the RootHandle.

The app takes every layer, from the ISV onwards , analyses each model and the model element data in them. It then looks if the element is used by any other layer below it OR if the root element is used in any other layer below or in the same layer.

How to use the App:

Set the minimum access layer – If you dont care about the SYS /SYP then set the Layer id to that of the ISV. Ofcourse if you work from VAR and beyond, then you probably dont care about ISV / ISP. So look at the Layer table and set the minimum access layer
Set this in the Tag “MinApplicationLayer” inside the App.config file

Connection String – You dont need to set this up, unless you want to default to a specific Ax Database. This is good if you only care about one application. When the App runs, it will ask for user input for the database server and name. Not entering it will revert to the connection string in the App.config file

This is still a command line application, the output is still on the screen. But that’s the next phase of this app, to output the data to files or on screen. And also to make it power shell friendly

Ok, how the app works now:

Run the Exe in a command prompt: AxModel.Common.Exe (I will work on the name, I promise), put the database server, and the database name, and after a few verbose output data in a CSV format is spit on the screen.

Copy that to excel and it should be all good from there.

The verbose output also outputs the dependencies if you want to look at it, which may be helpful.

Limitations: If you are referencing objects from one model to another in code, like a method in another object is called from a class, then that will not be detected. This is mainly because cross reference will need to be looked at and is out of scope at this stage. I am focusing on installation dependencies

The code is available on GitHub and I should be updating it more as I conduct more tests and get more input about this.