Calling AIF Document Services Directly from X++ in Dynamics AX 2012
Published: March 18, 2021 at 6:20:14 PM UTC
In this article, I explain how to call Application Integration Framework document services in Dynamics AX 2012 directly from X++ code, emulating both inbound and outbound calls, which can make it significantly easier to find and debug errors in AIF code.
The information in this post is based on Dynamics AX 2012 R3. It may or may not be valid for other versions.
I was recently helping a customer implement an Application Integration Framework (AIF) inbound port for creating customers based on data they were receiving from another system. As Dynamics AX already provides the CustCustomer document service, which implements the logic for this, we decided to keep it simple and use the standard solution.
However, it soon turned out that there were a lot of problems getting the external system to generate XML that Dynamics AX would accept. The XML schema generated by Dynamics AX is a quite complex one and it also appears there are few bugs in Dynamics AX that sometimes causes it to reject XML that is schema-valid according to other tools, so all in all, it proved to be less simple than I'd thought.
During the endeavor, I often struggled to figure out what exactly the problem was with certain XML files because the error messages provided by AIF are less than informative. It was also tedious, because I had to wait for the external system to send a new message over MSMQ and then again for AIF to pick up the message and process it before I could see an error.
I therefore investigated whether it's possible to call the service code directly with a local XML file for somewhat speedier testing and it turns out that it is - and not only that, it's really simple to do and actually provides a lot more meaningful error messages.
The example job below reads a local XML file and tries to use it with the AxdCustomer class (which is the document class used by the CustCustomer service) to create a customer. You can make similar jobs for all the other document classes, for example AxdSalesOrder, if you need.
{
FileNameOpen fileName = @'C:\\TestCustomerCreate.xml';
AxdCustomer customer;
AifEntityKey key;
#File
;
new FileIoPermission(fileName, #IO_Read).assert();
customer = new AxdCustomer();
key = customer.create( XmlDocument::newFile(fileName).xml(),
new AifEndpointActionPolicyInfo(),
new AifConstraintList());
CodeAccessPermission::revertAssert();
info('Done');
}
The AifEntityKey object returned by the customer.create() method (which corresponds to the "create" service operation in AIF) contains information about which customer was created, among other things the RecId of the created CustTable record.
If what you're trying to test is an Outbound port instead or if you just need an example of how the XML should look like on the Inbound port, you can also use the document class to export a customer to a file instead by calling the read() method (corresponding to the "read" service operation) instead, like so:
{
FileNameSave fileName = @'C:\\TestCustomerRead.xml';
Map map = new Map( Types::Integer,
Types::Container);
AxdCustomer customer;
AifEntityKey key;
XMLDocument xmlDoc;
XML xml;
AifPropertyBag bag;
#File
;
map.insert(fieldNum(CustTable, AccountNum), ['123456']);
key = new AifEntityKey();
key.parmTableId(tableNum(CustTable));
key.parmKeyDataMap(map);
customer = new AxdCustomer();
xml = customer.read(key,
null,
new AifEndpointActionPolicyInfo(),
new AifConstraintList(),
bag);
new FileIoPermission(fileName, #IO_Write).assert();
xmlDoc = XmlDocument::newXml(xml);
xmlDoc.save(fileName);
CodeAccessPermission::revertAssert();
info('Done');
}
You should of course replace '123456' with the account number of the customer you wish to read.