Wednesday 24 December 2014

How to update the InstanceRelationType field.

If you are using table inheritance in AX, you may have created base and derived tables. The InstanceRelationType field in the base table is an indicator that the record in base table belongs to which derived table. This field contains the TableId of the derived table. There can be scenarios in which you configure some sample/ demo data or meta data for your application in these tables and you may want this data to be available with each application installation. You might have generated some sql scripts/ or using Sql DTS(Data Tranformation Service) to copy that data for each installation.
The problem that you will face in that case is that for every fresh installation of your application on AX i.e. deployment of your code on AX, Your tables and all other objects are created with a new unique ID. A new tableId field is generated for each installation.
This will cause your setup data not to work with the new installation since the table ids are now different on the new environment so in the base table, AX can not identify which record belongs to which table.
You might think of manually checking the table id for all the derived tables and try to update them in an AX job by X++ code but AX will not allow you to update the InstanceRelationType  field.
The solution to update this field is to change it from Sql Server. A Sql script can be generated to update this field accordingly in the base table but even for that script, you will require the TableId/ Id fields for each derived table which you can get from AX. Getting the table id from AX is an additional step if you are working on AX R2 or R3. But if you are working on R1, then you can easily get the TableId from Sql as well.. which means you just have to execute a script with a click of a button and it will do all the work. Here is the sample Sql code to get the Table id field in R1.


declare @tableIdMyAxDerivedTable
as int = (select tableid from SQLDICTIONARY where NAME = 'MyAxDerivedTable and FIELDID = 0)

print @tableIdMyAxDerivedTable

After fetching the correct table id, lets update that in our data..

update MyBaseTable
set INSTANCERELATIONTYPE = @tableIdMyAxDerivedTable -- Current table id in AOT
where INSTANCERELATIONTYPE = 98789 -- Old table id in my data


To do the same task in AX R2 or R3... the solution is to create a job in AX, and place the above written sql update code in a string. You can easily fetch the table id from AX and replace that fetched table id in that sql query.
You can print that string by calling the info method...All you have to do now is to run that string/Sql query in Sql server.

One method to convert any data type to String.

In AX, you can use the "strFmt" method. This method can convert any data type to string. There is no need to write separate methods for each data type if you want to convert them to string.


Here is some sample code. I have declared and initialized some default AX data types. These data types are converted to string, which is printed in the info log.

    int i;
    real r;
    date dt;
    CustomerTransactionType cct; //AX base enum
    
    i = 10;
    r = 5.5;
    dt = today();
    cct = CustomerTransactionType::FreeTextInvoice;
    
    
    info(strFmt('%1',i));
    info(strFmt('%1',r));
    info(strFmt('%1',dt));
    info(strFmt('%1',cct));

Tuesday 23 December 2014

Calling table insert and update methods conditionally.

In AX, there can be situations where you have to write code for table insert and update. Lets say you are exposing some AX logic by AIF or manipulating data with temp. tables.  In these situations, you may develop only one method both for Insert and update. All the table fields data can be passed to this method by parameters. The important question is when to conditionally execute the Insert and Update method. The answer is the RecID field. RecId is always greater than zero for any record that is being selected from the database. So for that condition you can call the update method. Similarly, if the table buffer is populated with all the fields except the RecID field, then it means that  you have just filled all those fields and Insert methods needs to be called for that condition. To implement this logic all you have to do is to write the update and insert method in a If condition like:

If(Table1.Recid  > 0 )
{
Table1.update();
}
Else
{
Table1.insert();
}

Monday 15 December 2014

How to change AX layer

If you want to change the current AX layer, lets say from USR to ISV then follow the following steps.

1) Open "Microsoft Dynamics AX Configuration Utility".

2) Under the "Developer" tab, there is a field named as "Application object layer to open". From a list      of available layers, change this field's value from USR to ISV.

3) Enter the development license code that you have for the ISV layer.

4) Restart the AX client, it will now be opened in the ISV layer.

All the AX objects that you will create now or import through any XPO, will be created on the ISV layer.

XPO versus Model file

There can be scenarios where you have to decide either to deploy the AX objects by XPO or by a model file.
Deploying the objects by a XPO is a good option if you are working in a development/test environment and you want to  deploy your objects in another developer's system.
Where as,deploying the objects by a model file is a good option if you want to deploy/distribute the objects/solution to customers.

The following two cases must be taken care of while deciding to use XPO or Model file.

1) The XPO does not have the information of which change is related to which model, so whatever objects you import using the XPO, those objects will be  related the model you are currently working in. Lets suppose if you create a XPO on the ISV layer and then import this XPO on the target environment which is currently running on USR layer  then the imported objects will be created on the USR layer and not on the ISV layer. But if you want to specify the specific layer where your changes must be deployed then using the model file is the option. In that case, If you import a model file on an environment which is currently running on some other layer different than the imported models layer, then your changes will not be deployed on the current layer, but on the layer which was mentioned in the model file.

2) In deploying AX objects with a XPO, you can have a problem in the following scenario.
Lets suppose the project you want to deploy contains some table which have relations with each other. for example. Table A has relations with table B and table B has relations with table C.
In that case, after importing the XPO you will notice that there can be many errors that will come upon compilation. The reason of all the errors is that the table relations are not properly created  at all for some dependent tables. AX can not create a relation for table B in table A if table B is not created yet in the import procedure. Even after the table B is created, the relation in table A for table B is not updated/ fixed, not even if you manually restore and compile table A. As a result of this, you will get compilation error at every point  where ever the table buffer is used in your XPO's  classes or tables.
To fix that scenario, all you have to do is to import the XPO tables again. Importing the tables again will properly create the table relations thus fixing all the related errors. The occurrence of this problem comes only with XPO import/export. This problem can be avoided if you consider using a model file. Importing a model file one time will create all the table relations properly and there will be no need to import the model file again to fix the errors.

How to resolve unbalanced ttsBegin/ttsCommit X++ statements

If you have written some code in AX that have an uneven number of ttsBegin/ttsCommit statements or while running the code, after the execution of ttsBegin, some run time error comes and the rest of code which contains the matching ttsCommit statement is not executed. This results in an AX error message of unbalanced X++ ttsBegin/ttsCommit pair.
To solve this error, one solution is just to restart the AOS service. Another solution is to execute the following code in a job.

static void FixTTS(Args _args) 

    while (appl.ttsLevel() > 0) 
    { 
        info(strfmt("Level %1 aborted",appl.ttsLevel())); 
        ttsAbort; 
    } 
}


To avoid this error in future, care must be taken to write equal number of ttsBegin and ttsCommit statements. Also, its better to use ttsBegin/ttsCommit in a try catch block. So that we can use the throw statement in case of an error. The throw statement automatically initiates a ttsAbort which is a database transaction rollback i.e all changes in the current transaction are being discarded.

Monday 8 December 2014

How to increase maximum buffer size in AX 2012

While opening any AX form or report that uses a large number of data-sources and joins, you can get an error message which is similar to:

"The total, internal size of the records in your joined Select statement is 29374 bytes, but Microsoft Dynamics is by default performance-tuned not to exceed 27646 bytes. It is strongly recommended that you split your tables(s) into smaller units."


If you increase the maximum buffer size, then this error will stop occurring. To increase the maximum buffer size,
1) Open the "Microsoft Dynamics AX 2012 Server Configuration".
2) Create a new configuration if one is not not present by going to the "Manage" button and clicking "Create configuration".
3) Under the "Database Tuning" tab, Adjust the value of "Maximum buffer size (KB):"
4) Apply and save the changes.

This value must be increased in small increments. For example, 1 KB at a time, after applying the changes you should see its effects. Keep increasing the value in small increments till the error message no longer comes.
This is the safe way to increase the value, other wise the risk is, if you increase the value too much, lets say you update the value from 24 KB to 124 KB then this might result in frequent AX crashes.







What to do when AX starts behaving weird.

If you notice that AX, or any functionality in AX is not working as it should be, then here is a list of general options that might help you in fixing that issue.

1) Restart the AOS service and open AX client again. This fixes the issues that can come by an unbalanced TTSBEGIN and TTSCOMMIT statement.

2) Clear the AX cache files. These files can get corrupted and can interfere in normal running of AX. To delete AX cache files, you can search your C drive with all the files with .AUC extension. Stop the AOS. Delete all the AX related cache files with .AUC extension. Start the AOS.

Friday 23 May 2014

Calling an External Web Service from X++ AX 2012

If you want to call an external web service from AX 2012, then you have to do these two tasks:


1) Create the service reference in Visual studio.
2) Calling the Service from AX.


Creating a Service in VS:



  • Open Visual Studio 2010 and create a new Visual C# Class Library project. Name the project as WeatherTest.
  • In the Solution Explorer window, right-click the project name and then click Add Service Reference.
  • Type the URL for the web service in the Address field. Here, we are using a test service: http://www.webservicex.net/globalweather.asmx?WSDL
  • We are naming this service reference to ServiceReferenceName
  • Click Go to locate the service and then click Ok.
  • Add this solution to the AOT by using the Visual Studio Tools for Microsoft Dynamics AX 2012. The WeatherTest Project will start appearing in the AOT under the node Visual Studio Projects --> C Sharp Projects.

      
Calling the Service from AX:

Create a new job and paste the following code in it. The following example demonstrates how to call the web service. We are calling service’s GetWeather method by passing the parameters Karachi and Pakistan. It returns the weather details XML for the supplied parameters.


static void Job_CallWeatherService(Args _args)
{   
ClrObject clientType;
WeatherTest.ServiceReferenceName.GlobalWeatherSoapClient _client;
str ResultSet;   
System.Exception ex;
    try
   {
  //Construct and configure the service client by retrieving the X++ type for the service and                   //using the AifUtil class Retrieve the X++ type for the service client object.
   clientType = CLRInterop::getType("WeatherTest.ServiceReferenceName.GlobalWeatherSoapClient");
       // Use the AifUtil class to create an instance of the service client object.
       _client = AifUtil::CreateServiceClient(clientType);
       ResultSet = _client.GetWeather('karachi','Pakistan');
       info(ResultSet);
   }
   catch(Exception::CLRError)
   {
     ex = CLRInterop::getLastException();
     info(ex.ToString());
   }
}




Note: When writing this code in any class, it should be considered that "the Code that consumes a Web service must run on the server. Otherwise you will receive an error."